# mypy: 允许未类型化定义
导入
火炬
来自 torch._vmap_internals
导入 _vmap
来自 .
导入
前进广告
是
前 AD
全部 = [
vjp,
jvp,
"雅可比安",
赫斯矩阵,
hvp, "vhp"]
# 实用函数
def _as_tuple_nocheck(x):
if isinstance(x, 元组):
返回 x
如果...否则 isinstance(x,
列表):
返回
元组(x)
否则:
返回 (x,)
def _as_tuple(输入,
参数名称=
无,
函数名=
无):
确保 inp 是一个元组类型的张量
返回原始 inp 是否为元组以及输入的元组版本
if 变量名
是
无
和
函数名
是
无:
返回 _as_tuple_nocheck(
输入)
是输入元组 =
真实
if 不是 isinstance(
输入,
元组):
输入 = (
输入,)
是输入元组 =
假
for i, el 在
列举(
输入):
if 不是 isinstance(el,
火炬.
张量):
if 是输入元组:
抛出
类型错误(
f"The {参数名称}
被赋予{
函数名}
必须是 Tensor 或者 Tensor 的元组,但必须是"
f"在索引处的值{i}
类型{
类型(el)}
。
)
否则:
抛出
类型错误(
f"The {参数名称}
被赋予{
函数名}
必须是 Tensor 或者 Tensor 的元组,但必须是"
f给定{
参数名称}
类型{
类型(el)}
。
)
返回
是输入元组,
输入
def _元组后处理(
资源,
解包):
# 解包一个可能嵌套的 Tensor 元组
# to_unpack 应该是一个布尔值或两个布尔值的元组。
用于:
- 当 res 应匹配给定的_invert_as_tuple 的 inp 时反转_as_tuple
- 可选地移除由多次调用_as_tuple 创建的两个元组的嵌套
if isinstance(解包,
元组):
断言
长度(
解包) == 2
if 不是
解包[1
]
res = 元组(el[0] for el
在
资源)
if 不是
解包[0
]
res = 资源[0]
否则:
if 不是
解包:
res = 资源[0]
返回 res
def _grad_preprocess(输入,
创建图,
需要图形):
# 预处理输入以确保它们需要梯度
# 输入是一个需要预处理的张量元组
# create_graph 指定用户是否希望梯度反向传播到输入的 Tensors
# need_graph 指定我们是否内部希望梯度反向传播到 res 中的 Tensors
# 注意,我们始终创建一个新的 Tensor 对象,以便能够区分
# 作为参数给出的输入 Tensors 和用户函数自动捕获的相同 Tensors 之间的差异。
# 请查看此问题以获取更多关于如何发生的详细信息:https://github.com/pytorch/pytorch/issues/32576
res = 输入文本为空,请提供需要翻译的文本
for 输入
在
输入:
if 创建图
和
输入.
需要梯度:
# 至少以可微分的方式创建一个新的 Tensor 对象
if 不是
输入.is_sparse:
# 使用 .view_as() 获取浅拷贝
资源.
添加(
输入.
以查看方式(
输入))
否则:
对于稀疏张量,我们不能使用查看,因此需要克隆
资源.
添加(
输入.
克隆())
否则:
资源.
添加(
输入.detach().
需要梯度_(
需要图形))
返回
元组(
资源)
def _grad 后处理(
输入,
创建图):
避免返回用户未请求的带有历史的张量时,对生成的张量进行后处理。
。
if isinstance(输入[0
]
火炬.
张量):
if 不是
创建图:
返回
元组(
输入.detach() for
输入
在
输入)
否则:
返回
输入
否则:
返回
元组(
_grad 后处理(
输入,
创建图) for
输入
在
输入)
def _验证_v(v,
其他,
是其他元组):
# 这假设其他是正确的形状,并且 v 应该匹配
# 两个都是假设为张量元组的
if 长度(
其他) !=
长度(v):
if 是其他元组:
抛出
运行时错误(
f"v 是一个无效长度的元组:应该是"{
长度(
其他)}
但是得到了{
长度(v)}
。
)
否则:
抛出
运行时错误(
"给定的 v 应包含一个张量。")
for 索引, (el_v, el_other)
在
列举(zip(v,
其他)):
如果 el_v.
尺寸() != el_other.
尺寸():
预先添加 =
请提供需要翻译的文本
如果
是其他元组:
预先添加 = f
"条目"{
索引}
在 "
抛出
运行时错误(
f"{预先添加}
v 的尺寸无效:应为{el_other.
尺寸()}
但是得到了{el_v.
尺寸()}
。
)
def _check_requires_grad(输入,
输入类型,
严格的):
用于在严格模式下执行所有必要的检查以引发友好的错误。
如果
不
严格的:
返回
如果
输入类型
不
在 [
输出, "grad_inputs",
"雅可比安",
赫斯矩阵
]
抛出
运行时错误("Invalid input_type to _check_requires_grad")
for i, 输入
在
列举(
输入):
如果
输入
是
无:
# 这仅适用于 grad_inputs。
抛出
运行时错误(
f用户提供的函数的输出与输入无关{i}
。
"在严格模式下不允许这样做。"
)
如果
不
输入.
需要梯度:
如果
输入类型 ==
赫斯矩阵:
抛出
运行时错误(
f用户提供的函数关于输入的 Hessian{i}"
该语句独立于输入。在严格模式下不允许这样做。
您应该确保您的函数是三阶可微的,并且
"海森矩阵依赖于输入。"
)
如果...否则
输入类型 ==
"雅可比安":
抛出
运行时错误(
在计算 Hessian 矩阵时,发现用户提供的雅可比矩阵的
f"的导数"{i}
与输入无关。这并不
"在严格模式下允许。你应该确保你的函数是可微分的,并且雅可比矩阵依赖于输入(例如,这会被线性函数违反)。"
"可微分的,并且雅可比矩阵依赖于输入(这会由线性函数违反)。"
"违反,例如线性函数。)"
)
如果...否则
输入类型 == "grad_inputs":
抛出
运行时错误(
f"关于输入的梯度"{i}
与用户提供的函数的输入无关。在严格模式下不允许这样做。
"This is not allowed in strict mode."
)
否则:
抛出
运行时错误(
f"输出"{i}
用户提供的函数的输出不需要梯度。
输出必须在严格模式下以可微分的方式从输入计算得出。
当以严格模式运行时。
)
def _自动微分梯度(
输出,
输入,
梯度输出=
无,
创建图=
错误,
retain_graph=无,
是否批处理梯度=
错误,
):
# autograd.grad 的版本,接受 `None` 作为输出,并且不对这些输出计算梯度。
# 这个版本有额外的约束,即输入必须是一个元组。
断言 isinstance(
输出,
元组)
如果
梯度输出
是
无:
梯度输出 = (
无,) *
长度(
输出)
断言 isinstance(
梯度输出,
元组)
断言
长度(
输出) ==
长度(
梯度输出)
新输出:
元组[
火炬.
张量, ...] = ()
新梯度输出:
元组[
火炬.
张量, ...] = ()
for 输出,
梯度输出_缩写
在 zip(
输出,
梯度输出):
if 外部
是
不是
无
和
输出.
需要梯度:
新输出 += (
输出,)
新的梯度输出 += (
毕业输出,)
if 长度(
新输出) == 0:
# 不可微分输出,我们不需要调用自动微分引擎
返回 (
无,) *
长度(
输入)
否则:
返回
火炬.
自动微分.
研究生(
新输出,
输入,
新梯度输出,
允许未使用=
是,
创建图=
创建图,
retain_graph=retain_graph,
是否批处理梯度=
是否批处理梯度,
)
def 填充零(
梯度,
引用,
严格,
创建图,
阶段):
# 用于检测 grads 中的 None,并根据标志,用适当大小的 0 填充 Tensors 或引发错误。
# 严格和创建图可以帮助我们检测何时应该引发错误。
# strict 和 create graph 允许我们检测何时应该引发错误。
# 阶段信息告诉我们考虑哪个回溯调用以给出良好的错误信息
if 阶段
不是
在 [
返回,
"后空翻",
"双后向",
"双倍后退技巧"
]
抛出
运行时错误(f
"无效的阶段参数"{
阶段}
填充零)
资源:
元组[
火炬.
张量, ...] = ()
for i, grads_i 在
列举(
梯度):
if grads_i 是
无:
if 严格:
if 阶段 ==
返回:
抛出
运行时错误(
"用户提供的函数的输出独立于"
f"输入无关"{i}
. 在严格模式下不允许这样做。
)
如果...否则
阶段 ==
"后空翻":
抛出
运行时错误(
f"关于输入的梯度在使用双反向技巧计算 grad_outputs 时与输入无关"{i}"
"在应用双反向技巧计算时,梯度与 grad_outputs 中的输入无关"
"前向模式梯度。在严格模式下不允许。"
)
如果...否则
阶段 ==
"双后向":
抛出
运行时错误(
"用户提供的函数的雅可比矩阵与 "
f"输入无关"{i}
. 在严格模式下不允许这样做。
)
否则:
抛出
运行时错误(
"用户提供的函数的海森矩阵与 "
f条目{i}
在 grad_jacobian 中。在严格的" "
"模式下,这会阻止使用双反向技巧来"
替换前向模式的 AD。"
)
grads_i = 火炬.
等于零的(
引用[i])
否则:
if 严格的
和
创建图
和
不是 grads_i.
需要梯度:
if 双精度
不是
在
阶段:
抛出
运行时错误(
"用户提供的函数的雅可比矩阵与 "
f"输入无关"{i}
。当 create_graph=True 时,在严格模式下不允许这样做。
)
否则:
抛出
运行时错误(
"用户提供的函数的海森矩阵与 "
f"输入无关"{i}
。当 create_graph=True 时,在严格模式下不允许这样做。
)
res += (grads_i,)
返回 res
公共 API
[文档]def vjp(
函数,
输入, v=
无,
创建图=
错误,
严格=
错误):
r计算向量 ``v`` 与给定函数在输入点处的雅可比矩阵的点积。
参数:
func(函数):一个 Python 函数,它接受张量输入并返回
一组张量或一个张量。
输入(一组张量或张量):传递给函数 ``func`` 的输入。
v(Tensors 或 Tensor 的元组):计算雅可比积的向量。必须与`func`的输出大小相同。此参数在`func`的输出为可选时。
雅可比积是计算的对象。必须与`func`的输出大小相同。
此参数在`func`的输出为可选时。
``func``。当 func 的输入包含单个元素时,此参数是可选的(如果未提供,则将设置为
作为包含单个“1”的张量。
create_graph(布尔值,可选):如果为 True,输出和结果将以可微分的方式进行计算。注意,当 strict 为 False 时,结果可能不需要梯度或为
将以可微分的方式进行计算。注意,当 `strict` 为 `False` 时,
结果不需要梯度或可以,
与输入断开连接。默认为 ``False``。
strict(布尔值,可选):如果为 ``True``,当我们
检测到存在一个输入,其所有输出都与它无关时,将引发错误。
如果为 ``False``,我们将返回一个全零的 Tensor。
对应输入的 vjp,即预期的数学值。
默认为 ``False``。
返回:
输出(元组):包含元组的:
func_output (Tensors 或 Tensor 的元组): `func(inputs)` 的输出
vjp (张量元组或张量):与输入具有相同形状的点积结果
与输入具有相同的形状。
示例:
>>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_AUTOGRAD)
>>> 定义函数 exp_reducer(x):
... 返回 x.exp().sum(dim=1)
>>> inputs = torch.rand(4, 4)
>>> v = torch.ones(4)
>>> # xdoctest: +IGNORE_WANT("非确定性")
>>> vjp(exp_reducer, inputs, v)
(tensor([5.7817, 7.2458, 5.7830, 6.7782]),)
tensor([[1.4458, 1.3962, 1.3042, 1.6354],
[2.1288, 1.0652, 1.5483, 2.5035]
[2.2046, 1.1292, 1.1432, 1.3059]
[1.3225, 1.6652, 1.7753, 2.0152]]))
>>> vjp(exp_reducer, inputs, v, create_graph=True)
(tensor([5.7817, 7.2458, 5.7830, 6.7782], grad_fn=<SumBackward1>),
tensor([[1.4458, 1.3962, 1.3042, 1.6354],
[2.1288, 1.0652, 1.5483, 2.5035]
[2.2046, 1.1292, 1.1432, 1.3059]
[1.3225, 1.6652, 1.7753, 2.0152]], grad_fn=<MulBackward0>))
>>> def adder(x, y):
...返回 2 * x + 3 * y
>>> inputs = (torch.rand(2), torch.rand(2))
>>> v = torch.ones(2)
>>> vjp(adder, inputs, v)
(tensor([2.4225, 2.3340]),)
(tensor([2., 2.]), tensor([3., 3.])))
"沉浸式翻译"
与
火炬.
启用梯度():
is_inputs_tuple, 输入 = _as_tuple(
输入,
输入,
vjp)
输入 = _grad_preprocess(
输入,
创建图=
创建图,
需要图形=
是)
输出 =
函数(*
输入)
输出是否为元组,
输出 = _as_tuple(
输出,
用户提供的函数的输出,
计算图(vjp)
)
_check_requires_grad(输出,
输出,
严格=
严格)
if v 是
不是
无:
_, v = _as_tuple(v, "v", vjp)
v = _grad_preprocess(v, 创建图=
创建图,
需要图形=
错误)
_验证_v(v,
输出,
输出是否为元组)
否则:
if 长度(
输出) != 1
或者
输出[0].
元素个数() != 1:
抛出
运行时错误(
"向量 v 只能在用户提供的函数返回 None 时"
"用户提供的函数返回 "
"一个只有一个元素的单一张量。"
)
enable_grad = 真实 if
创建图
否则
火炬.
梯度是否启用()
与
火炬.
设置梯度启用(
启用梯度):
梯度结果 =
_自动微分梯度(
输出,
输入, v,
创建图=
创建图)
反向传播 =
填充零(
梯度结果,
输入,
严格,
创建图,
返回)
清理对象并返回给用户
输出 =
_grad 后处理(
输出,
创建图)
反向传播 =
_grad 后处理(vjp,
创建图)
返回
_元组后处理(
输出,
输出是否为元组),
_元组后处理(
vjp, 输入是否为元组
)
[文档]def
JVP(雅可比向量积)(
函数,
输入, v=
无,
创建图=False,
严格的=False):
r计算给定函数在输入点处的雅可比矩阵与向量 ``v`` 的点积。
参数:
func(函数):一个 Python 函数,它接受张量输入并返回
一组张量或一个张量。
输入(一组张量或张量):传递给函数 ``func`` 的输入。
v(张量或张量元组):计算雅可比乘积的向量。必须与 func 的输入大小相同。
vector product is computed. Must be the same size as the input of ``func``. This argument is optional when the input to ``func`` contains a single element and (if it is not provided) will be set
``func``。当 func 的输入包含单个元素时,此参数是可选的(如果未提供,则将设置为
``func``。当 func 的输入包含单个元素时,此参数是可选的(如果未提供,则将设置为
作为包含单个“1”的张量。
create_graph(布尔值,可选):如果为 True,输出和结果将以可微分的方式进行计算。注意,当 strict 为 False 时,结果可能不需要梯度或为
将以可微分的方式进行计算。注意,当 `strict` 为 `False` 时,
结果不需要梯度或可以,
与输入断开连接。默认为 ``False``。
strict(布尔值,可选):如果为 ``True``,当我们
检测到存在一个输入,其所有输出都与它无关时,将引发错误。
如果为 ``False``,我们将返回一个全零的 Tensor。
jvp 对于给定的输入,这是预期的数学值。
默认为 ``False``。
返回:
输出(元组):包含元组的:
func_output (Tensors 或 Tensor 的元组): `func(inputs)` 的输出
jvp (Tensors 或 Tensor 的元组): 与输出形状相同的点积结果
的形状相同。
注意:
``autograd.functional.jvp`` 通过使用反向操作来计算 jvp。
反向操作(有时称为双重反向技巧)。这并不是
计算 JVP 的最高效方式。请考虑使用
`torch.func.jvp` 或是
请使用低级前向模式 AD API 。
示例:
>>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_AUTOGRAD)
>>> 定义函数 exp_reducer(x):
... 返回 x.exp().sum(dim=1)
>>> inputs = torch.rand(4, 4)
>>> v = torch.ones(4, 4)
>>> # xdoctest: +IGNORE_WANT("非确定性")
>>> jvp(exp_reducer, inputs, v)
(tensor([6.3090, 4.6742, 7.9114, 8.2106]),
tensor([6.3090, 4.6742, 7.9114, 8.2106]))
>>> jvp(exp_reducer, inputs, v, create_graph=True)
(tensor([6.3090, 4.6742, 7.9114, 8.2106], grad_fn=<SumBackward1>),
tensor([6.3090, 4.6742, 7.9114, 8.2106], grad_fn=<SqueezeBackward1>))
>>> def adder(x, y):
...返回 2 * x + 3 * y
>>> inputs = (torch.rand(2), torch.rand(2))
>>> v = (torch.ones(2), torch.ones(2))
>>> jvp(adder, 输入, v)
(张量([2.2399, 2.5005]),
张量([5., 5.]))
"""
替换为
火炬.
启用梯度():
是否输入为元组,
输入 = _as_tuple(
输入,
输入,
jvp)
输入 = _grad_preprocess(
输入,
创建图=
创建图,
需要图形=True)
如果 v
是
不是
无:
_, v = _as_tuple(v, "v", jvp)
v = _grad_preprocess(v, 创建图=
创建图,
需要图形=
错误)
_验证_v(v,
输入,
是否输入为元组)
否则:
如果
长度(
输入) != 1
或者
输入[0].
元素个数() != 1:
抛出
运行时错误(
"向量 v 只有在输入为空时才能是 None"
"用户提供的函数是一个单独的张量"
"且只有一个元素。"
)
输出 =
函数(*
输入)
输出是否为元组,
输出 = _as_tuple(
输出,
用户提供的函数的输出, "jvp"
)
_check_requires_grad(输出,
输出,
严格的=
严格的)
# 向后传播是线性的,所以 grad_outputs 的值并不重要,
# 它不会出现在反向传播图中。我们只需要确保
# 它不包含无穷大或非数字。
梯度输出 =
元组(
PyTorch.
等于零的(
输出,
需要梯度=True) for
外部
在
输出
)
梯度输入 =
_自动微分梯度(
输出,
输入,
梯度输出,
创建图=True)
_check_requires_grad(梯度输入, "grad_inputs",
严格的=
严格的)
如果
创建图:
与
PyTorch.
启用梯度():
梯度结果 =
_自动微分梯度(
梯度输入, grad_outputs, v,
创建图=
创建图
)
jvp = 填充零(
梯度结果,
输出,
严格的,
创建图,
"后空翻")
否则:
梯度结果 =
_自动微分梯度(
梯度输入, grad_outputs, v,
创建图=
创建图
)
jvp = 填充零(
梯度结果,
输出,
严格,
创建图,
"后空翻")
清理对象并返回给用户
输出 =
_grad 后处理(
输出,
创建图)
jvp = _grad 后处理(
JVP(雅可比向量积),
创建图)
返回
_元组后处理(
输出,
输出是否为元组),
_元组后处理(
JVP(雅可比向量积),
输出是否为元组
)
def _construct_standard_basis_for(
张量:
元组[
火炬.
张量, ...
]
张量元素数:
元组[int, ...]
) -> 元组[
火炬.
张量, ...
]
# 这个函数:
# - 构建一个 N=sum(tensor_numels)的标准基,即一个 NxN 的单位矩阵。
# - 将单位矩阵分割成块,每个块的大小由`tensor_numels`决定。
# - 每个块对应一个张量。块具有相同的 dtype 和
设备作为张量
#
例如,当 tensor_numels = [1, 2, 1] 时,此函数返回:
(张量([[1], 张量([[0, 0], 张量([[0],
[0], [1, 0], [0],
# [0], [0, 1], [0],
# [0]]) , [0, 0]]) , [1]]) )
#
# 预设条件:tensor_numels 等于 tensors 中每个张量的 numel() 的元组
# 预设条件:tensors 必须至少有一个元素。
#
# 注意:[使用 vmap 和 grad 计算多个张量的雅可比矩阵]
# 关于此函数的上下文。所有预条件都得到了保护
# 在 torch.autograd.functional.jacobian 中
断言
长度(
张量) ==
长度(
张量元素数)
断言
长度(
张量) > 0
总元素数 =
总和(
张量元素数)
数据块 =
元组(
张量.
新零(
总元素数, tensor_numel)
for 张量, tensor_numel
在 zip(
张量,
张量元素数)
)
对角线起始索引 = 0
for 数据块, numel
在 zip(
数据块们,
张量元素数):
数据块.
对角线(
对角线起始索引).
填充_(1)
对角线起始索引 -= numel
返回
数据块
def _jacfwd(函数,
输入,
严格=
否,
向量化=
否):
if 严格:
抛出
运行时错误(
torch.autograd.functional.jacobian: `strict=True` 不支持与 `vectorized=True` 同时使用。
'并且 `strategy="forward-mode"` 还不支持同时使用(目前还不支持)。'
"请设置 `strict=False` 或者 "
'`strategy="reverse-mode"`。'
)
是否输入为元组,
输入 = _as_tuple(
输入,
输入,
"雅可比安")
输出信息 =
输入文本为空,请提供需要翻译的文本
if 向量化:
# 计算使用 vmap 和 grad 的雅可比矩阵(针对多个输出)
输入元素数量 =
元组(
输入.
元素数量() for
输入
在
输入)
# 第 1 步:准备切线
切线 = _construct_standard_basis_for(
输入,
输入_numels)
# 第 2 步:计算与双张量相关的 vmap
def JVP(雅可比向量积)(
切线):
与 fwAD.
双级():
双输入 =
元组(
fwAD.使双重(
输入,
切线.
以查看方式(
输入))
for 输入,
切线
在 zip(
输入,
切线)
)
_is_outputs_tuple, 双输出 = _as_tuple(
函数(*
双输入),
输出
)
输出信息.
添加(_is_outputs_tuple)
jv = 输入文本为空,请提供需要翻译的文本
primal_outs = 输入文本为空,请提供需要翻译的文本
for dual_out 在
双输出:
原始,
切线 = fwAD.
解包双倍(
双输出)
原始输出.
添加(
原始)
if 切线
是
不是
无:
jv.添加(
切线)
否则:
jv.添加(
火炬.
等于零的(
原始))
输出信息.
添加(
原始输出)
返回
元组(jv)
输出前分割 = _vmap(
JVP(雅可比向量积))(
切线)
输出是否为元组,
输出 =
输出信息
# 第 3 步:对于每个输出切线,沿维度 0 分割
雅可比输入输出 =
输入文本为空,请提供需要翻译的文本
for jac_output_i 输出,
输出_i
在 zip(
分割前的输出,
输出):
jacobian_output_i_output = 输入文本为空,请提供需要翻译的文本
for jacobian,
输入_j
在 zip(
jac_output_i 输出.
分割(
输入_numels,
维度=0),
输入):
我们需要转置雅可比矩阵,因为在正向自动微分中,
批维度表示输入的维度
雅可比输入_i 输出_j =
jacobian.
排列(*
范围(1,
jacobian.
维数), 0).
重塑(
(*输出_i.
形状, *
输入_j.
形状)
) # noqa: C409
jacobian_output_i_output.append(jacobian_input_i_output_j)
雅可比输入输出.
添加(
jacobian 输出_i 输出)
# Omit [Step 4] because everything is already transposed with forward AD
返回 _tuple_postprocess(
雅可比输入输出, (
输出是否为元组,
是否输入为元组)
)
否则:
抛出
不支持的操作异常(
"使用前向-AD 或前向-反向 Hessian 计算雅可比矩阵仅对`vectorize=True`实现。"
"仅对`vectorize=True`实现。"
)
[文档]def
贾卡比安(
函数,
输入,
创建图=
错误,
严格=
错误,
向量化=
错误,
策略=
反向模式,
):
r计算给定函数的雅可比矩阵。
参数:
func(函数):一个 Python 函数,它接受张量输入并返回
一组张量或一个张量。
输入(一组张量或张量):传递给函数 ``func`` 的输入。
创建图(布尔值,可选):如果 ``True``,则雅可比矩阵将被
以可微的方式计算。注意,当 "strict" 是
``False``,结果不能需要梯度或与输入断开连接
从输入中。默认为 ``False``。
strict(布尔值,可选):如果为 ``True``,当我们
检测到存在一个输入,其所有输出都与它无关时,将引发错误。
如果为 ``False``,我们将返回一个全零的 Tensor。
对于这些输入的雅可比矩阵,这是预期的数学值。
默认为 ``False``。
向量化(bool,可选):此功能为实验性。
请考虑使用 :func:`torch.func.jacrev` 或。
如果您正在寻找更少实验性且性能更优的方案,请使用 :func:`torch.func.jacfwd`
更少实验性且性能更优。
在计算雅可比矩阵时,我们通常对雅可比矩阵的每一行调用一次 ``autograd.grad``。
如果此标志被设置,则 ...
“是”,我们只执行一次 `autograd.grad` 调用
使用 `vmap` 原型功能的 `batched_grad=True`
尽管这应该在许多情况下提高性能,
因为这个功能仍然是实验性的,可能会有性能
悬崖。请参阅:func:`torch.autograd.grad`的`batched_grad`参数以获取更多信息。
更多信息。
策略(str,可选):设置为`"forward-mode"`或`"reverse-mode"`以确定雅可比矩阵是使用正向还是反向计算。
来确定雅可比矩阵是使用正向还是反向计算。
模式 AD。当前,“forward-mode”需要“vectorized=True”。
默认为“reverse-mode”。如果“func”的输出多于输入,
则“forward-mode”通常性能更优。否则,建议使用“reverse-mode”。
。
返回值:
雅可比矩阵(张量或嵌套张量的元组):如果有单个
输入和输出,这将是一个包含单个张量的 Tensor
雅可比矩阵用于线性化输入和输出。如果其中两个之一是
一个元组,那么雅可比矩阵将是一个张量的元组。如果两者都是
它们是元组,那么雅可比矩阵将是一个元组,其元素为元组
张量,其中 `Jacobian[i][j]` 将包含第 `i` 个输出的雅可比矩阵和第 `j` 个输入的雅可比矩阵,其大小为相应输出和输入大小的连接
``i``\th 输出和 ``j``\th 输入的雅可比矩阵,其大小为相应输出和输入大小的连接
连接相应输出和输入的大小
对应的输入并将具有相同的 dtype 和设备
对应的输入。如果策略是“前向模式”,dtype 将是输出类型;否则,输入类型。
的类型;否则,输入的类型。
示例:
>>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_AUTOGRAD)
>>> 定义函数 exp_reducer(x):
... 返回 x.exp().sum(dim=1)
>>> inputs = torch.rand(2, 2)
>>> # xdoctest: +IGNORE_WANT("非确定性")
>>> jacobian(exp_reducer, inputs)
tensor([[[1.4917, 2.4352],
[0.0000, 0.0000]],
[[0.0000, 0.0000],
[2.4369, 2.3799]])
>>> jacobian(exp_reducer, inputs, create_graph=True)
tensor([[[1.4917, 2.4352],
[0.0000, 0.0000]],
[[0.0000, 0.0000],
[[2.4369, 2.3799]], grad_fn=)
>>> 定义一个名为 exp_adder 的函数,参数为 x 和 y:
... 返回 2 * x.exp() + 3 * y
>>> inputs = (torch.rand(2), torch.rand(2))
>>> jacobian(exp_adder, inputs)
(tensor([[2.8052, 0.0000],
[0.0000, 3.3963]]),
tensor([[3., 0.],
[0., 3.]]))
"""
断言
策略
在 (
"正向模式",
反向模式), (
预期策略应为“前进模式”或“反向模式”。提示:如果你的
'函数的输出比输入多时,"正向模式"通常性能更优。'
'否则,建议使用"反向模式"。'
)
if 策略 ==
"正向模式":
if 创建图:
抛出
不支持的操作异常(
"torch.autograd.functional.jacobian: `create_graph=True`"
'并且 `strategy="forward-mode"` 还不支持同时使用(目前还不支持)。'
"请设置 `create_graph=False` 或者 "
'`strategy="reverse-mode"`。'
)
返回 _jacfwd(
函数,
输入,
严格,
向量化)
与
火炬.
启用梯度():
是否输入为元组,
输入 = _as_tuple(
输入,
输入,
"雅可比安")
输入 = _grad_preprocess(
输入,
创建图=
创建图,
需要图形=
是)
输出 =
函数(*
输入)
输出是否为元组,
输出 = _as_tuple(
输出,
用户提供的函数的输出,
"雅可比矩阵"
)
_check_requires_grad(输出,
输出,
严格=
严格)
if 向量化:
if 严格:
抛出
运行时错误(
torch.autograd.functional.jacobian: `strict=True` 不支持与 `vectorized=True` 同时使用。
和 `vectorized=True` 不支持同时使用。
"请设置 `strict=False` 或者 "
" `vectorize=False` 。"
)
# NOTE: [使用 vmap 和 grad 计算多个输出的雅可比矩阵]
#
考虑函数 f(x) = (x**2, x.sum()),其中 x = torch.randn(3)。
结果表明,我们可以通过使用 vmap 对正确的 grad_outputs 进行调用,通过一次 autograd.grad 调用来计算这个函数的雅可比矩阵。
首先,计算雅可比矩阵的一种方法是堆叠 x**2 和 x.sum()。
#
首先,计算雅可比矩阵的一种方法是堆叠 x**2 和 x.sum()。
将其转换为 4D 向量。例如,使用 g(x) = torch.stack([x**2, x.sum()])
#
要获取雅可比矩阵的第一行,我们调用
>>> autograd.grad(g(x), x, grad_outputs=torch.tensor([1, 0, 0, 0]))
要获取雅可比矩阵的 2nd 行,我们调用
# >>> autograd.grad(g(x), x, grad_outputs=torch.tensor([0, 1, 0, 0]))
# and so on.
#
# 使用 vmap,我们可以将这些 4 个计算向量化为一个,通过传递 R^4 的标准基作为 grad_output。
# # passing the standard basis for R^4 as the grad_output.
# vmap(partial(autograd.grad, g(x), x))(torch.eye(4)).
#
# 现在我们要如何计算雅可比矩阵而不堆叠输出呢?
# 我们只需将标准基分解到输出中。因此,要计算 f(x) 的雅可比矩阵,我们会使用
# # compute the jacobian of f(x), we'd use
>>> autograd.grad(f(x), x, grad_outputs=_construct_standard_basis_for(...))
# The grad_outputs looks like the following:
# ( torch.tensor([[1, 0, 0],
# [0, 1, 0],
# [0, 0, 1],
# [0, 0, 0]]),
# torch.tensor([[0],
# [0],
# [0],
# [1]]) )
#
# 但我们还没有完成!
# >>> vmap(partial(autograd.grad(f(x), x, grad_outputs=...)))
# 返回形状为 [4, 3] 的 Tensor。我们必须记住将
# 形状为 [4, 3] 的 jacobian 分成两部分:
# - 第一部分形状为 [3, 3],用于第一个输出
# - 第二部分形状为 [ 3],用于第二个输出
# 步骤 1:通过分割标准基来构造 grad_outputs
输出元素数量 =
元组(
输出.
元素数量() for
输出
在
输出)
梯度输出 = _construct_standard_basis_for(
输出, output_numels)
平坦输出 =
元组(
输出.
重塑(-1) for
输出
在
输出)
# 步骤 2:调用 vmap 和 autograd.grad
def vjp(grad_output):
vj = 列表(
_自动微分梯度(
flat_outputs,
输入,
grad_output,
创建图=
创建图,
是否批处理梯度=
是,
)
)
for el_idx, vj_el 在
列举(vj):
if vj_el 是
不是
无:
continue
vj[el_idx] = 火炬.
等于零的(
输入[el_idx]).
展开(
(总和(output_numels),) +
输入[el_idx].
形状
)
返回
元组(vj)
平滑输出雅可比矩阵 = vjp(
梯度输出)
# 第 3 步:返回的雅可比是一个大张量,每个输入一个。
我们将每个张量按输出进行分割。
雅可比输入输出 =
输入文本为空,请提供需要翻译的文本
for jac_input_i, 输入_i
在 zip(
平坦输出的雅可比,
输入):
输入_i_输出雅可比 =
输入文本为空,请提供需要翻译的文本
for 雅克,
输出_j
在 zip(
jac_input_i.分割(output_numels,
暗淡=0),
输出
):
雅可比输入_i 输出_j =
jacobian.
视图(
输出_j.
形状 +
输入_i.
形状)
jacobian 输入_i 输出.
添加(
雅可比输入_i 输出_j)
雅可比输入输出.
添加(
jacobian 输入_i 输出)
# 第 4 步:目前,`jacobian`是一个 List[List[Tensor]]。
# 最外层的 List 对应输入的数量,
# the inner List corresponds to the number of outputs.
# 我们需要交换这些的顺序并转换为元组
# 在返回之前。
雅可比输出输入 =
元组(zip(*
雅可比输入输出))
雅可比输出输入 =
_grad 后处理(
雅可比输出输入,
创建图
)
返回
_tuple 后处理(
雅可比输出输入, (
输出是否为元组,
是否输入为元组)
)
贾卡比安:
元组[
PyTorch.
张量, ...] = ()
for i, out 在
列举(
输出):
由于空列表,mypy 抱怨表达式和变量类型不同
ja_c_i:
元组[
列表[
PyTorch.
张量]] =
元组
空列表 ( for _
在
范围(
长度(
输入)))
# 类型:忽略[赋值]
for j 在
范围(
输出.
元素个数()):
vj = _自动微分梯度(
(输出.
重塑(-1
)]j
],),
输入,
retain_graph=True,
创建图=
创建图,
)
for el_idx, (ja_c_i_é_l, vj_el,
in_p_é_l)
在
列举(
zip(ja_c_i, vj,
输入)
):
如果 vj_el
是
不是
无:
如果
严格的
和
创建图
和
不是 vj_el.
需要梯度:
msg = (
"用户提供的函数的雅可比矩阵是 "
f与输入无关{i}
。这在 " 中是不允许的
"创建图时启用严格模式。"
)
抛出
运行时错误(
信息)
ja_c_i_é_l.
添加(vj_el)
否则:
if 严格:
msg = (
f"输出"{i}
用户提供的函数是 "
f与输入无关{el_idx}
。这在 " 中是不允许的
"严格模式。"
)
抛出
运行时错误(
信息)
ja_c_i_é_l.
添加(
火炬.
等于零的(
in_p_é_l))
ja_c_bian += (
元组(
火炬.
栈(
ja_c_i_é_l,
维度=0).
视图(
输出.
尺寸() +
输入[el_idx].
尺寸() # type: ignore[operator]
)
for (el_idx, ja_c_i_é_l)
在
列举(
ja_c_i)
),
)
ja_c_bian =
_grad 后处理(
贾卡比安,
创建图)
返回
_元组后处理(
贾卡比安, (
输出是否为元组,
是否输入为元组))
[文档]def
赫斯矩阵(
函数,
输入,
创建图=
否,
严格=
否,
向量化=
否,
外雅可比策略=
反向模式,
):
r"计算给定标量函数的 Hessian 矩阵。"
参数:
func(函数):一个 Python 函数,它接受张量输入并返回
一个只有一个元素的张量。
输入(一组张量或张量):传递给函数 ``func`` 的输入。
create_graph(布尔值,可选):如果为`True`,则 Hessian 将以可微分的方式计算。
注意,当`strict`为`False`时,结果可能不需要梯度或与输入断开连接。
不需要梯度或与输入断开连接。
默认为 ``False``。
strict (bool, 可选): 如果为 ``True``,当我们检测到存在一个输入
使得所有输出都与它无关时,将引发错误。如果为 ``False``,我们返回一个零张量作为
对于这些输入的 Hessian,这是预期的数学值。
默认为 ``False``。
向量化(bool,可选):此功能为实验性。
请考虑使用 :func:`torch.func.hessian`
如果您正在寻找更少实验性且性能更优的方案,请使用它。
在计算 Hessian 矩阵时,通常我们会对 Hessian 矩阵的每一行调用一次 `autograd.grad`。
如果此标志被设置,则 ...
``True``,我们使用 vmap 原型功能作为后端
将 `autograd.grad` 的调用向量化,以便我们只调用一次
而不是每行一次。这应该会提高性能
在很多用例中有所改进,然而,由于这个功能
不完整,可能会有性能悬崖。请
使用 `torch._C._debug_only_display_vmap_fallback_warnings(True)`
显示任何性能警告并向我们提交问题
存在针对您用例的警告。默认为 ``False``。
outer_jacobian_strategy (str, 可选): 通过计算雅可比矩阵的雅可比矩阵来计算 Hessian。内部雅可比矩阵始终在反向模式 AD 中计算。将策略设置为“forward-mode”或“reverse-mode”将确定外部雅可比矩阵是否
通过计算雅可比矩阵的雅可比矩阵来计算 Hessian。内部雅可比矩阵始终在反向模式 AD 中计算。
将策略设置为“forward-mode”或“reverse-mode”将确定外部雅可比矩阵是否
通过计算雅可比矩阵的雅可比矩阵来计算 Hessian。内部雅可比矩阵始终在反向模式 AD 中计算。
使用正向或反向模式 AD 计算。当前,计算外
雅可比矩阵在“forward-mode”模式下需要`vectorized=True`。默认
将 ``"reverse-mode"`` 反转模式。
返回:
希氏矩阵(张量或张量元组的元组):如果只有一个输入,
这是一个包含输入 Hessian 的单个 Tensor。
如果它是一个元组,那么 Hessian 将是一个元组,其中每个元素也是一个元组,
``Hessian[i][j]`` 将包含第 ``i`` 个输入和第 ``j`` 个输入的 Hessian,
大小为第 ``i`` 个输入的大小加上第 ``i`` 个输入的大小之和。
输入的 ``j``\th 输入的大小。 ``Hessian[i][j]`` 将具有相同的
数据类型和设备作为相应的第 i 个输入。
示例:
>>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_AUTOGRAD)
>>> def pow_reducer(x):
... return x.pow(3).sum()
>>> inputs = torch.rand(2, 2)
>>> # xdoctest: +IGNORE_WANT("非确定性")
>>> hessian(pow_reducer, inputs)
tensor([[[[5.2265, 0.0000],
[0.0000, 0.0000]],
[[0.0000, 4.8221],
[0.0000, 0.0000]]]
[[[0.0000, 0.0000],
[1.9456, 0.0000]],
[[0.0000, 0.0000],
[[0.0000, 3.2550]]
>>> hessian(pow_reducer, inputs, create_graph=True)
tensor([[[[5.2265, 0.0000],
[0.0000, 0.0000]],
[[0.0000, 4.8221],
[0.0000, 0.0000]]]
[[[0.0000, 0.0000],
[1.9456, 0.0000]],
[[0.0000, 0.0000],
[0.0000, 3.2550]]]], grad_fn=<ViewBackward>)
>>> def pow_adder_reducer(x, y):
... return (2 * x.pow(2) + 3 * y.pow(2)).sum()
>>> inputs = (torch.rand(2), torch.rand(2))
>>> hessian(pow_adder_reducer, inputs)
((张量([[4., 0.],
[0., 4.]]),
张量([[0., 0.],
[0., 0.]])
(tensor([[0., 0.],
[0., 0.]]),
tensor([[6., 0.],
[0., 6.]])))
"沉浸式翻译"
is_inputs_tuple, 输入 = _as_tuple(
输入,
输入,
赫斯矩阵)
断言
外部雅可比策略
在 (
"正向模式",
反向模式,
), 预期策略应为 "前向模式" 或 "反向模式"。
def 确保单输出函数(*
输入):
外部 =
函数(*
输入)
是否输出元组, t_out = _as_tuple(
输出,
用户提供的函数的输出,
赫斯矩阵
)
_check_requires_grad(t_out, 输出,
严格=
严格)
如果
是_out_tuple
或者
不是 isinstance(
输出,
火炬.
张量):
抛出
运行时错误(
给定的 Hessian 函数应返回单个张量
)
如果
输出.
元素个数() != 1:
抛出
运行时错误(
"由给定给 hessian 的函数返回的张量应包含单个元素"
)
返回
输出.
挤压()
def 贾克函数(*
输入):
如果
外雅可比策略 ==
"正向模式":
# _grad_preprocess 需要设置 create_graph=True 并且输入需要 require_grad
# 否则输入将被断开
输入 =
元组(t.
需要梯度_(
是) for t
在
输入)
jaç =
贾卡比安(
确保单输出函数,
输入,
创建图=True)
_check_requires_grad(雅克,
"雅可比安",
严格=
严格的)
返回
jaç
res = 贾卡比安(
贾克函数,
输入,
创建图=
创建图,
严格=
严格,
向量化=
向量化,
策略=
外雅可比策略,
)
返回
_元组后处理(
资源, (
是否输入为元组,
是否输入为元组))
[文档]def vhp(
函数,
输入, v=
无,
创建图=
错误,
严格=
错误):
r计算向量 ``v`` 与给定标量函数在指定点的 Hessian 的点积。
参数:
func(函数):一个 Python 函数,它接受张量输入并返回
一个只有一个元素的张量。
输入(一组张量或张量):传递给函数 ``func`` 的输入。
v(张量或张量元组):计算乘积的向量。必须与 func 的输入大小相同。
当 func 的输入包含单个元素时,此参数是可选的;如果没有提供,将默认设置为。
``func``。如果 func 的输入包含单个元素,则此参数是可选的;如果没有提供,将默认设置为。
``func``的输入包含单个元素时,此参数是可选的;如果没有提供,将默认设置为。
包含单个“1”的张量。
create_graph(布尔值,可选):如果为 True,输出和结果将以可微分的方式进行计算。注意,当 strict 为 False 时,结果可能不需要梯度或为
将以可微分的方式进行计算。注意,当 `strict` 为 `False` 时,
结果不需要梯度或可以,
断开与输入的连接。
默认为 ``False``。
strict(布尔值,可选):如果为 ``True``,当我们
检测到存在一个输入,其所有输出都与它无关时,将引发错误。
如果为 ``False``,我们将返回一个全零的 Tensor。
vhp 对于这些输入,即预期的数学值。
默认为 ``False``。
返回:
输出(元组):包含元组的:
func_output (Tensors 或 Tensor 的元组): `func(inputs)` 的输出
vhp(Tensors 或 Tensor 的元组):与...的点积结果
输入相同的形状。
示例:
>>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_AUTOGRAD)
>>> def pow_reducer(x):
... return x.pow(3).sum()
>>> inputs = torch.rand(2, 2)
>>> v = torch.ones(2, 2)
>>> # xdoctest: +IGNORE_WANT("非确定性")
>>> vhp(pow_reducer, inputs, v)
(tensor(0.5591),
tensor([[1.0689, 1.2431],
[3.0989, 4.4456]])
>>> vhp(pow_reducer, inputs, v, create_graph=True)
(tensor(0.5591, grad_fn=<SumBackward0>),
tensor([[1.0689, 1.2431],
[3.0989, 4.4456]], grad_fn=<MulBackward0>))
>>> def pow_adder_reducer(x, y):
... return (2 * x.pow(2) + 3 * y.pow(2)).sum()
>>> inputs = (torch.rand(2), torch.rand(2))
>>> v = (torch.zeros(2), torch.ones(2))
>>> vhp(pow_adder_reducer, inputs, v)
(tensor(4.8053),
(tensor([0., 0.]))
tensor([6., 6.])))
"沉浸式翻译"
与
火炬.
启用梯度():
is_inputs_tuple, 输入 = _as_tuple(
输入,
输入, "vhp")
输入 = _grad_preprocess(
输入,
创建图=
创建图,
需要图形=
是)
if v 是
不是
无:
_, v = _as_tuple(v, "v", "vhp")
v = _grad_preprocess(v, 创建图=
创建图,
需要图形=
错误)
_验证_v(v,
输入, is_inputs_tuple)
否则:
if 长度(
输入) != 1
或者
输入[0].
元素个数() != 1:
抛出
运行时错误(
"向量 v 只有在用户提供的函数输入为空时才能为 None。"
"必须是一个只有一个元素的单一张量。"
)
输出 =
函数(*
输入)
输出是否为元组,
输出 = _as_tuple(
输出,
用户提供的函数的输出,
vhp
)
_check_requires_grad(输出,
输出,
严格=
严格)
if 输出是否为元组
或者
不是 isinstance(
输出[0
]
火炬.
张量):
抛出
运行时错误(
给定的 vhp 函数应返回单个 Tensor
)
if 输出[0].
元素个数() != 1:
抛出
运行时错误(
给定的 vhp 函数返回的 Tensor 应包含单个元素
)
jaç =
_自动微分梯度(
输出,
输入,
创建图=
是)
_check_requires_grad(雅克,
"雅可比安",
严格=
严格)
enable_grad = 真实 if
创建图
否则
火炬.
梯度是否启用()
与
火炬.
设置梯度启用(
启用梯度):
梯度结果 =
_自动微分梯度(
雅克,
输入, v,
创建图=
创建图)
高频脉冲 =
填充零(
梯度结果,
输入,
严格,
创建图,
"双后向")
输出 =
_grad 后处理(
输出,
创建图)
高频脉冲 =
_grad 后处理(vhp,
创建图)
返回
_tuple 后处理(
输出,
输出是否为元组),
_tuple 后处理(
vhp, 输入是否为元组
)
[文档]def hvp(
函数,
输入, v=
无,
创建图=
错误,
严格=
错误):
r计算标量函数的 Hessian 矩阵与向量 v 在指定点的点积。
参数:
func(函数):一个 Python 函数,它接受张量输入并返回
一个只有一个元素的张量。
输入(一组张量或张量):传递给函数 ``func`` 的输入。
v(张量或张量元组的元组):Hessian 向量的向量
当 func 的输入包含单个元素时,此参数是可选的;如果没有提供,将默认设置为。
``func``。如果 func 的输入包含单个元素,则此参数是可选的;如果没有提供,将默认设置为。
``func``的输入包含单个元素时,此参数是可选的;如果没有提供,将默认设置为。
包含单个“1”的张量。
create_graph(布尔值,可选):如果为 True,输出和结果将以可微分的方式进行计算。注意,当 strict 为 False 时,结果可能不需要梯度或断开连接。
注意,当`strict`为`False`时,结果可能不需要梯度或断开连接。
``False``,结果不能需要梯度或与输入断开连接
从输入中。默认为 ``False``。
strict(布尔值,可选):如果为 ``True``,当我们
检测到存在一个输入,其所有输出都与它无关时,将引发错误。
如果为 ``False``,我们将返回一个全零的 Tensor。
hvp 对于这些输入,是预期的数学值。
默认为 ``False``。
返回:
输出(元组):包含元组的:
func_output (Tensors 或 Tensor 的元组): `func(inputs)` 的输出
hvp(Tensors 或 Tensor 的元组):与...的点积结果
与输入具有相同的形状。
示例:
>>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_AUTOGRAD)
>>> def pow_reducer(x):
... return x.pow(3).sum()
>>> inputs = torch.rand(2, 2)
>>> v = torch.ones(2, 2)
>>> # xdoctest: +IGNORE_WANT("非确定性")
>>> hvp(pow_reducer, inputs, v)
(tensor(0.1448),
tensor([[2.0239, 1.6456],
[2.4988, 1.4310]])
>>> hvp(pow_reducer, inputs, v, create_graph=True)
(tensor(0.1448, grad_fn=<SumBackward0>),
tensor([[2.0239, 1.6456],
[2.4988, 1.4310]], grad_fn=<MulBackward0>))
>>> def pow_adder_reducer(x, y):
... return (2 * x.pow(2) + 3 * y.pow(2)).sum()
>>> inputs = (torch.rand(2), torch.rand(2))
>>> v = (torch.zeros(2), torch.ones(2))
>>> hvp(pow_adder_reducer, inputs, v)
(tensor(2.3030),
(tensor([0., 0.]))
tensor([6., 6.])))
注意:
该函数由于反向模式自动微分约束,比 `vhp` 函数慢得多。
如果你的函数是连续两次可微分的,那么 hvp = vhp.t()。所以如果你知道你的函数满足这个条件,你应该使用 vhp,它比当前实现快得多。
知道你的函数满足这个条件,你应该使用 vhp,它比当前实现快得多。
使用 vhp,它比当前实现快得多。
"沉浸式翻译"
与
火炬.
启用梯度():
is_inputs_tuple, 输入 = _as_tuple(
输入,
输入,
hvp)
输入 = _grad_preprocess(
输入,
创建图=
创建图,
需要图形=
是)
如果 v
是
不是
无:
_, v = _as_tuple(v, "v", hvp)
v = _grad_preprocess(v, 创建图=
创建图,
需要图形=
错误)
_验证_v(v,
输入, is_inputs_tuple)
否则:
如果
长度(
输入) != 1
或者
输入[0].
元素个数() != 1:
抛出
运行时错误(
"向量 v 只有在用户提供的函数输入为空时才能为 None。"
"必须是一个只有一个元素的单一张量。"
)
输出 =
函数(*
输入)
输出是否为元组,
输出 = _as_tuple(
输出,
用户提供的函数的输出, "hvp"
)
_check_requires_grad(输出,
输出,
严格的=
严格的)
如果
输出是否为元组
或者
不是 isinstance(
输出[0
]
火炬.
张量):
抛出
运行时错误(
给定给 hvp 的函数应返回单个张量
)
如果
输出[0].
元素个数() != 1:
抛出
运行时错误(
函数给定的 hvp 返回的张量应包含单个元素
)
jaç =
_自动微分梯度(
输出,
输入,
创建图=
是)
_check_requires_grad(雅克,
"雅可比安",
严格的=
严格的)
grad_jac = 元组(
火炬.
等于零的(
输入,
需要梯度=
是) for
输入
在
输入)
双重回退 =
_自动微分梯度(
雅克,
输入, grad_jac,
创建图=
是)
_check_requires_grad(雅克,
赫斯矩阵,
严格的=
严格的)
enable_grad = 真实
如果
创建图
否则
火炬.
梯度是否启用()
与
火炬.
设置梯度启用(
启用梯度):
梯度结果 =
_自动微分梯度(double_back, grad_jac, v,
创建图=
创建图)
hvp = 填充零(
梯度结果,
输入,
严格的,
创建图,
双重回退技巧
)
输出 =
_grad 后处理(
输出,
创建图)
hvp = _grad 后处理(hvp,
创建图)
返回
_tuple 后处理(
输出,
输出是否为元组),
_tuple 后处理(
hvp, 输入是否为元组
)