Skip to main content

一个 pytorch 库,允许用户在跨越训练循环而不是单个训练步骤的损失上获得更高阶的梯度。

项目描述

更高的标志

higher是一个为高阶优化提供支持的库,例如通过展开的一阶优化循环,这些循环的“元”方面。它提供了将现有torch.nn.Module实例变为“无状态”的工具,这意味着可以跟踪对其参数的更改,并且可以采用关于中间参数的梯度。它还提供了一套可微优化器,以促进各种元学习方法的实施。

完整文档可在https://higher.readthedocs.io/en/latest/获得。

要求和安装

  • Python 版本 >= 3.5
  • PyTorch 版本 >= 1.3

higherPyPi安装:

pip install higher

higher从源安装:

git clone git@github.com:facebookresearch/higher.git
cd higher
pip install .

或者python setup.py install会做同样的事情。

引文

如果您higher在研究中使用并发现它有帮助,请考虑引用以下论文:

@article{grefenstette2019generalized,
  title={Generalized Inner Loop Meta-Learning},
  author={Grefenstette, Edward and Amos, Brandon and Yarats, Denis and Htut, Phu Mon and Molchanov, Artem and Meier, Franziska and Kiela, Douwe and Cho, Kyunghyun and Chintala, Soumith},
  journal={arXiv preprint arXiv:1910.01727},
  year={2019}
}

用例

您的需求

您有一个modelwith parameters P,其中P[t]表示更新时间步的参数t。您希望通过k优化步骤更新模型,并通过优化过程计算梯度,即计算torch.autograd.grad(P[k], P[0])或获得依赖于现有梯度路径的梯度。

你的障碍

您正在为您modelP[t]. 即使您推出自己的解决方案,您也希望使用普通 SGD 之外的优化技术,而torch.optim优化器不会让您“通过”它们进行优化。

您的解决方案

好消息:higher让你得到保障!使用我们不断增长的工具和实用功能集,您可以通过无限数量的模型更新步骤进行反向传播,以满足您的所有元学习需求。该库包括:

  • 猴子修补torch.nn模块的帮助函数,使它们具有功能性(无状态),即在前向传递期间将它们的参数作为额外参数提供。
  • 实现(和 SGD)的可区分版本的类torch.optim.Adam,旨在跟踪或从“正常”Adam实例的状态分支出来。

示例用法

假设您的训练代码如下所示:

model = MyModel()
opt = torch.optim.Adam(model.parameters())

for xs, ys in data:
    opt.zero_grad()
    logits = model(xs)
    loss = loss_function(logits, ys)
    loss.backward()
    opt.step()

要将其转换为可微分版本,应引入以下更改:

model = MyModel()
opt = torch.optim.Adam(model.parameters())

# When you want to branch from the current state of your model and unroll
# optimization, follow this example. This context manager gets a snapshot of the
# current version of the model and optimizer at the point where you want to
# start unrolling and create a functional version `fmodel` which executes the
# forward pass of `model` with implicit fast weights which can be read by doing
# `fmodel.parameters()`, and a differentiable optimizer `diffopt` which ensures
# that at each step, gradient of `fmodel.parameters()` with regard to initial
# fast weights `fmodel.parameters(time=0)` (or any other part of the unrolled
# model history) is defined.

with higher.innerloop_ctx(model, opt) as (fmodel, diffopt):
    for xs, ys in data:
        logits = fmodel(xs)  # modified `params` can also be passed as a kwarg
        loss = loss_function(logits, ys)  # no need to call loss.backwards()
        diffopt.step(loss)  # note that `step` must take `loss` as an argument!
        # The line above gets P[t+1] from P[t] and loss[t]. `step` also returns
        # these new parameters, as an alternative to getting them from
        # `fmodel.fast_params` or `fmodel.parameters()` after calling
        # `diffopt.step`.

        # At this point, or at any point in the iteration, you can take the
        # gradient of `fmodel.parameters()` (or equivalently
        # `fmodel.fast_params`) w.r.t. `fmodel.parameters(time=0)` (equivalently
        # `fmodel.init_fast_params`). i.e. `fast_params` will always have
        # `grad_fn` as an attribute, and be part of the gradient tape.

    # At the end of your inner loop you can obtain these e.g. ...
    grad_of_grads = torch.autograd.grad(
        meta_loss_fn(fmodel.parameters()), fmodel.parameters(time=0))

请注意,当像这样展开优化时k,模型在每一步的所有梯度和所有激活都保存在内存中,这意味着模型的内存占用要k大几倍。

添加您自己的优化器

可以使用除torch.optim. 必须首先实现可微分版本。这可以通过继承higher.optim.DifferentiableOptimizer和覆盖_update原始方法的参数来完成。假设要添加的优化器的逻辑遵循 中的逻辑,torch.optim那么要遵循的步骤或多或少:

  1. 删除以下代码(不支持闭包)。
    loss = None
    if closure is not None:
        loss = closure()
    
  2. 代替
    for group in self.param_groups:
        for p in group['params']:
            if p.grad is None:
                continue
            grad = p.grad.data
    
    zipped = zip(self.param_groups, grouped_grads)
    for group_idx, (group, grads) in enumerate(zipped):
        for p_idx, (p, g) in enumerate(zip(group['params'], grads)):
          if g is None:
              continue
    
  3. 替换state = self.state[p]state = self.state[group_idx][p_idx]
  4. 用非就地操作替换任何就地操作,例如t.add_(a, x).mul_(y)应该成为t = t.add(a, x).mul(y)(注意分配)。还要小心跟踪这些操作隐式更新字典的位置,例如,如果有以下形式的代码:
    p = state['k']
    ...
    p.add_(a, x)
    
    在原始优化器中,此代码应转换为
    p = state['k']
    ...
    state['k'] = p = p.add(a, x)
    
    确保对应的字典是。
  5. 除了用于形状推断的地方,t.datatfor all替换 的实例t
  6. 确保group['params'][p_idx]为每个p_idx需要更新的内容进行更新(忽略的将在快速权重集合中产生原始参数)。step继承的函数将返回最新的快速权重。
  7. 重要的是,您需要higher使用 using注册新的可微优化器,higher.register_optim以确保它被库的方法识别为一个选项。您可以在定义优化器之后的任何时候执行此操作,并且在higher调用涉及该优化器的任何代码之前。例如,如果您已经实现MyDiffOpt了某个优化器的可微分版本,请通过在定义类之后MyOpt添加行来注册它。higher.register_optim(MyOpt, MyDiffOpt)

您可以在 中找到有关如何使用有限差分方法测试梯度正确性的示例tests/test_optim.py。请注意,可能需要一些稳定性技巧来避免nan梯度中的 s。请参阅higher.optim.DifferentiableAdam实施以获取缓解策略的示例,例如识别产生爆炸梯度的操作,例如通常采用移动平均值的平方根(初始为零)的操作,并使用帮助程序在这些函数x.register_hook的输入上注册一个反向钩子x函数_get_mask_closurehigher.optim.

发行说明

有关发行说明,请参阅更改日志

已知/可能的问题

  • 请参阅问题跟踪器以获取最新列表。
  • 目前没有支持(或计划支持)torch.nn.DataParallel。这将需要重写DataParallel. 如果这对您很重要,请在 pytorch 问题跟踪器上提出问题。
  • 一些自适应梯度式可微优化器可能不稳定,并且在采用更高阶梯度时会产生 NaN。已经使用了一些技巧来降低这种风险。如果这些在实践中还不够,请提出问题。
  • 二阶梯度可能不适用于某些 CUDNN 模块(主要是 RNN)。从 PyTorch v1.3 开始,higher使用以下上下文管理器包装使用模型的代码应该可以解决问题:
with torch.backends.cudnn.flags(enabled=False):
    # Your meta-learning code here...

执照

higher在 Apache 许可证版本 2.0 下发布。

谢谢

感谢Adam Paszke ,他的要点torch.nn是我们猴子修补任意模块 的方法的灵感来源(和起点) 。

感谢许多实习生、研究人员和工程师帮助路测这个库的早期版本。

项目详情


下载文件

下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。

源分布

Higher-0.2.1.tar.gz (25.0 kB 查看哈希)

已上传 source

内置分布

Higher-0.2.1-py3-none-any.whl (27.3 kB 查看哈希

已上传 py3