快捷键

torch.autograd.graph 的源代码

导入 abc
导入 contextlib
导入 functools
导入 记录日志
导入 线程
来自 集合 导入 默认字典, 双端队列
来自 collections.abc 导入 生成器, 迭代器, 迭代器, 可变映射, 序列
来自 打字 导入 (
    任何,
    可调用,
    角色,
    直接,
    命名元组,
    可选,
    类型检查,
    联合,
)
来自 typing_extensions 导入 类型别名
来自 弱引用 导入 WeakKeyDictionary, 弱值字典

导入 火炬
来自 torch.autograd.variable 导入 变量
来自 torch.utils._python_dispatch 导入 Torch 分发模式
来自 torch.utils.hooks 导入 可移除句柄


如果 类型检查:
    来自 torch._ops 导入 运算符重载


全部 = [
    "保存张量钩子",
    "在 CPU 上保存",
    "禁用保存张量钩子",
    "注册多梯度钩子",
    "允许在保存的 tensor 上执行修改",
    "Node",
    "梯度边",
    "获取梯度边",
    "增加版本",
]


日志 = 记录.获取日志记录器(__name__)


 节点(abc.ABC):
[文档] @abc.抽象方法 def 名称(self) -> str: 返回名称。 示例:: 导入 torch 库 创建一个需要梯度的 tensor:a = torch.tensor([0., 0., 0.], requires_grad=True) >>> b = a.clone() >>> assert isinstance(b.grad_fn, torch.autograd.graph.Node) >>> print(b.grad_fn.name()) CloneBackward0 "``" 抛出未实现异常
@property @abc.抽象方法 def 下一个函数
(self) -> 元组[元组[可选["Node"] int] ...] 抛出 未实现异常
[文档] @abc.abstractmethod def 元数据(self) -> dict: r"""返回元数据。""" 抛出未实现异常
@property @abc.抽象方法 def 输入元数据
(self) -> 列表[任何] 抛出 未实现异常 @abc.抽象方法 def _注册钩子字典(self, 张量: PyTorch.张量) -> : 抛出 未实现异常
[文档] @abc.abstractmethod def 注册钩子(self, fn: Callable[..., Any]) -> RemovableHandle: r"""注册反向钩子。 每次计算相对于梯度的导数时,都会调用此钩子。 节点已计算。钩子应具有以下签名:: hook(grad_inputs: 张量元组, grad_outputs: 张量元组) -> 张量元组或 None 钩子不应修改其参数,但可以可选地返回 一个新的梯度,该梯度将用于替换 :attr:`grad_inputs`。 此函数返回一个带有方法 `handle.remove()` 的句柄 该方法从模块中移除钩子 .. 注意:: 有关此钩子何时执行的更多信息,请参阅 :ref:`backward-hooks-execution` 执行过程以及其相对于其他钩子的执行顺序。 .. 注意:: 在非常罕见的情况下,当钩子在节点已经开始执行时注册,就不再保证 :attr:`grad_outputs`。 在钩子在节点已经开始执行时注册的罕见情况下,不再有关于 :attr:`grad_outputs` 的任何保证。 内容(可能因其他因素而正常或为空)。 钩子仍然可以选择性地返回一个新梯度,以替换使用 :attr:`grad_inputs` 与 :attr:`grad_outputs` 独立。 示例: >>> 导入 torch 库 >>> a = torch.tensor([0., 0., 0.], requires_grad=True) >>> b = a.clone() >>> assert isinstance(b.grad_fn, torch.autograd.graph.Node) >>> handle = b.grad_fn.register_hook(lambda gI, gO: (gO[0] * 2,)) >>> b.sum().backward(retain_graph=True) >>> print(a.grad) tensor([2., 2., 2.]) >>> handle.remove() # 移除钩子 >>> a.grad = None >>> b.sum().backward(retain_graph=True) >>> print(a.grad) tensor([1., 1., 1.]) """ 抛出未实现异常
[文档] @abc.abstractmethod def 注册预钩子(self, fn: Callable[..., Any]) -> RemovableHandle: r"""注册反向预钩子。 每次计算相对于梯度的导数时,都会调用此钩子。 节点已计算。钩子应具有以下签名:: 钩子应不修改其参数,但可以可选地返回 一个新的梯度,该梯度将用于替换 :attr:`grad_outputs`。 如果返回新的梯度,则它将被用于代替 :attr:`grad_outputs`。 该函数返回一个带有方法 `handle.remove()` 的句柄 该方法从模块中移除钩子 .. 注意:: 有关此钩子何时执行的更多信息,请参阅 :ref:`backward-hooks-execution` 执行过程及其相对于其他钩子的执行顺序。 示例:: >>> a = torch.tensor([0., 0., 0.], requires_grad=True) >>> b = a.clone() >>> 断言 b.grad_fn 是 torch.autograd.graph.Node 的实例 >>> handle = b.grad_fn.register_prehook(lambda gI: (gI[0] * 2,)) >>> b.sum().backward(retain_graph=True) >>> 打印(a.grad) tensor([2., 2., 2.]) >>> handle.remove() >>> a.grad = None >>> b.sum().backward(retain_graph=True) >>> 打印(a.grad) tensor([1., 1., 1.]) """ 抛出未实现异常
@classmethod def __subclasshook__(
, 子类: 类型) -> bool: 如果 节点 ( ( 子类 子类 getattr(PyTorch._C.函数, 子类.__name__, ) ) 或者 派生类(子类, PyTorch.自动微分.函数.向后 C 函数) ): 返回 真实 返回 NotImplemented def _获取梯度函数或梯度累积器(t: 联合[PyTorch.张量, "梯度边"]) -> 节点: 如果 isinstance(t, 梯度边缘): 返回 t.节点 如果 t.需要梯度 t.grad_fn : 替换为 PyTorch.启用梯度(): 节点 = t.以查看方式(t).grad_fn.下一个函数[0] [0] # 类型:忽略[联合属性] 否则: 节点 = t.grad_fn 断言 节点 返回 节点
[文档]类 GradientEdge(NamedTuple): 表示 autograd 图中给定梯度边的对象。 获取给定张量梯度将计算的梯度边缘 您可以使用 `edge = autograd.graph.get_gradient_edge(tensor)` 获取张量的梯度边。 ```python # 假设输入文本为: input_text = '"""' # 翻译函数(此处仅为示例,实际翻译功能需要调用真实的翻译 API) def translate_to_simplified_chinese(text): # 这里应该调用真实的翻译 API 进行翻译 # 由于示例中不使用真实的 API,以下为模拟翻译结果 return text # 输出翻译结果 translated_text = translate_to_simplified_chinese(input_text) print(translated_text) ``` node: 节点 output_nr: 整数
[docs]def get_gradient_edge(tensor: torch.Tensor) -> GradientEdge: """获取给定张量的梯度边,用于计算该张量的梯度。 特别是,它等价于调用 `g = autograd.grad(loss, input)` 和 `g = autograd.grad(loss, get_gradient_edge(input))`. ```python # 假设输入文本为: input_text = '"""' # 翻译函数(此处仅为示例,实际翻译功能需要调用真实的翻译 API) def translate_to_simplified_chinese(text): # 这里应该调用真实的翻译 API 进行翻译 # 由于示例中不使用真实的 API,以下为模拟翻译结果 return text # 输出翻译结果 translated_text = translate_to_simplified_chinese(input_text) print(translated_text) ``` 如果 tensor 不需要梯度 引发 RuntimeError( 无法获取张量的梯度边缘 "不需要梯度" ) grad_fn = _get_grad_fn_or_grad_acc(tensor) # Note that output_nr default to 0 which is the right value # for the AccumulateGrad node. return GradientEdge(grad_fn, tensor.output_nr)
[docs]def increment_version(tensor: Union[torch.Tensor, Iterable[torch.Tensor]]) -> None: """更新自动微分元数据跟踪给定的 Tensor 是否被就地修改。 这是为了在自动微分引擎中实现更精确的错误检查。 这已经由 PyTorch 函数和自定义 Function 自动完成。 当适当地调用 mark_dirty() 时,你只需显式调用此函数 如果你在以 PyTorch 不支持的方式对 Tensor 数据进行原地操作 了解。例如,一个自定义内核读取 Tensor 数据指针并修改 基于此指针的内存原地。可以接受一个张量或张量列表。 请注意,对单个就地操作多次增加版本计数器 不是问题。 请注意,如果传递在 torch.inference_mode()下构建的张量, 我们不会增加其版本计数器(因为你的张量没有版本计数器)。 """ 如果 tensor 是 torch.Tensor 类型: tensor = (tensor,) torch._C._increment_version(tensor)
[文档] 保存张量钩子: 上下文管理器,用于为保存的张量设置一对打包/解包钩子。 使用此上下文管理器来定义操作中间结果的打包方式,以便在保存前进行打包,并在检索时进行解包。 在此上下文中,每当进行操作时,都会调用 `pack_hook` 函数。 在此上下文中,`pack_hook` 函数将在每次保存张量之前被调用。 操作保存张量以供反向传播(这包括中间结果) 使用 func:`~torch.autograd.function._ContextMethodMixin.save_for_backward` 但 也包括由 PyTorch 定义的操作记录的输出)。 ``pack_hook``随后被存储在计算图中,而不是原始张量。 ``unpack_hook``在需要访问已保存的张量时被调用,即当执行:func:`torch.Tensor.backward()`时。 当需要访问已保存的张量时,会调用``unpack_hook``,即当执行:func:`torch.Tensor.backward()`时。 当需要访问已保存的张量时,会调用``unpack_hook``,即当执行:func:`torch.Tensor.backward()`时。 `torch.autograd.grad()` 函数接受一个 *打包* 对象作为参数。 由 `pack_hook` 返回的对象,并应返回一个与原始张量(作为对应 `pack_hook` 输入传递的张量)具有相同内容的张量。 `pack_hook` 的输入传递的张量)的内容。 `pack_hook` 的输入传递的张量)的内容。 插件应具有以下签名: pack_hook(tensor: Tensor) -> Any unpack_hook(Any) -> Tensor 其中 `pack_hook` 的返回值是 `unpack_hook` 的有效输入。 通常情况下,你希望从数值、大小、数据类型和设备等方面来看,`unpack_hook(pack_hook(t))` 等于 `t`。 在此方面。 示例:: >>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_AUTOGRAD) >>> def pack_hook(x): ... 打印("打包", x) ... 返回 x ... >>> 定义 unpack_hook(x): ... 打印("解包", x) ... 返回 x ... >>> a = torch.ones(5, requires_grad=True) >>> b = torch.ones(5, requires_grad=True) * 2 >>> with torch.autograd.graph.saved_tensors_hooks(pack_hook, unpack_hook): ... y = a * b 打包 tensor([1., 1., 1., 1., 1.], requires_grad=True) 打包 tensor([2., 2., 2., 2., 2.], grad_fn=) >>> y.sum().backward() 解包 tensor([1., 1., 1., 1., 1.], requires_grad=True) 解包 tensor([2., 2., 2., 2., 2.], grad_fn=) ..警告:: 对输入的 hooks 进行就地操作可能会导致未定义的行为。 可能导致未定义行为。 ..警告:: 一次只能使用一对钩子。当递归嵌套时 上下文管理器,只有最内层的钩子会被应用。 """ def __init__( self, 打包钩子: 可调用[[PyTorch.张量] 任何] 解包钩子: 可调用[[任何] PyTorch.张量] ) -> : self.打包钩子 = 打包钩子 self.解包钩子 = 解包钩子 def __进入__(self) -> : PyTorch._C._自动微分._push_saved_tensors_default_hooks( self.打包钩子, self.解包钩子 ) def __退出__(self, *参数: 对象) -> : PyTorch._C._自动微分.默认钩子保存 pop 张量()
[文档] 保存到 CPU(保存张量钩子): 在此上下文中管理器下,前向传递保存的张量将被存储在 CPU 上,然后用于反向检索。 当在此上下文中管理器内执行操作时,中间结果 结果将在前向传播过程中保存在图中,将被移动到 CPU 当需要反向传播时,会将其复制回原始设备。 如果图已经位于 CPU 上,则不执行张量复制。 使用此上下文管理器以 GPU 内存使用量换取计算(例如 当您的模型在训练过程中不适用于 GPU 内存时)。 参数: pin_memory(布尔值):如果为 ``True``,张量将被保存到 CPU 固定内存 在打包过程中异步复制到 GPU,在解包过程中异步从 GPU 复制。 默认为 ``False``。 也请参阅::ref:`cuda-memory-pinning`。 示例:: >>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_CUDA) >>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_AUTOGRAD) >>> a = torch.randn(5, requires_grad=True, device="cuda") >>> b = torch.randn(5, requires_grad=True, device="cuda") >>> c = torch.randn(5, requires_grad=True, device="cuda") ... >>> def f(a, b, c): ... prod_1 = a * b # a 和 b 存储在 GPU 上 ... with torch.autograd.graph.save_on_cpu(): ... prod_2 = prod_1 * c # prod_1 和 c 存储在 CPU 上 ... y = prod_2 * a # prod_2 和 a 存储在 GPU 上 ... 返回 y ... >>> y = f(a, b, c) >>> del a, b, c # 仅用于说明 >>> # a, b 和 prod_2 的内容在 GPU 上仍然存活 >>> # prod_1 和 c 的内容仅存在于 CPU 上 >>> y.sum().backward() # 所有 CPU 张量都移动回 GPU,用于反向传播 >>> # 在调用 backward()之后,所有中间张量都会被释放(删除) """ def __init__(self, 持久化内存: 布尔值 = False, 设备类型: 字符串 = cuda) -> : 设备模块 = getattr(PyTorch, 设备类型, PyTorch.cuda) def 打包到 CPU(张量: PyTorch.张量) -> 元组[PyTorch.设备, PyTorch.张量] 如果 持久化内存: 返回 (张量.设备, 张量.cpu()) 打包 = PyTorch.空的( 张量.尺寸(), 数据类型=张量.数据类型, 布局=张量.布局, 持久化内存=(设备模块.是否可用() 张量.is_sparse), ) 拼装.复制_(张量) 返回 (张量.设备, 拼装) def 从 CPU 解包(拼装: 元组[PyTorch.设备, PyTorch.张量]) -> PyTorch.张量: 设备, 张量 = 打包 返回 张量.(设备, 非阻塞=持久化内存) 超级().__init__(打包到 CPU, 从 CPU 解包)
[文档]@contextlib.contextmanager def disable_saved_tensors_hooks(error_message: str) -> 生成器[None, None, None]: """禁用保存张量默认钩子功能的上下文管理器。 适用于您正在创建一个不与保存的 张量默认钩子不起作用的特性。 参数: error_message (str): 当使用保存的张量默认钩子时, 已被禁用,出现了一个 RuntimeError 错误 错误信息被引发。 示例: >>> # xdoctest: +忽略(失败) >>> message = "已保存张量的默认钩子被禁用" >>> with torch.autograd.graph.disable_saved_tensors_hooks(message): ... # 抛出 RuntimeError: 已保存张量的默认钩子被禁用 ... with torch.autograd.graph.save_on_cpu(): ...通过 """ maybe_prev_message = None try: maybe_prev_message = ( torch._C._autograd._saved_tensors_hooks_get_disabled_error_message() ) torch._C._autograd._saved_tensors_hooks_disable(error_message) yield finally: # 注意:[disabled_error_message 不变] if maybe_prev_message is None: torch._C._autograd._saved_tensors_hooks_enable() else: torch._C._autograd._saved_tensors_hooks_disable(maybe_prev_message)
_MultiHandle(可移除句柄): 处理: 元组[可移除句柄, ...] def __init__(self, 处理: 元组[可移除句柄, ...]) -> : self.处理 = 处理 def 删除(self) -> : for handle self.处理: 处理.删除() def __getstate__(self) -> 元组[可移除句柄, ...] 返回 self.处理 def __setstate__(self, 状态: 元组[可移除句柄, ...]) -> : self.处理 = 状态
[文档]def 注册多梯度钩子( 张量: 序列[PyTorch.张量] 函数: 联合[ 可调用[[序列[可选[PyTorch.张量]], ] 可调用[[PyTorch.张量] ] ] *, 模式: 直接[所有, 任何] = 所有, ) -> 可移除句柄: r"注册一个多梯度反向钩子。" 有两种支持的模式:`"all"` 和 `"any"`。 在“all”模式下,钩子将在相对于每个张量的梯度之后被调用 `tensors` 已计算。如果一个张量在 :attr:`tensors` 中但 不是图的一部分,或者如果不需要张量来计算梯度 对于当前 ``.backward()`` 或 ``.grad()`` 调用指定的任何 ``inputs``,这个张量将被忽略,钩子将不会等待其梯度被计算。 在每个非忽略张量的梯度被计算后,:attr:`fn` 将被调用。 计算完毕。 在每个非忽略张量的梯度计算完毕后,:attr:`fn` 将被调用。 调用这些梯度。对于未传递张量的情况,将传递 ``None``。 计算它们的梯度。 在“any”模式下,钩子将在第一次梯度之后被调用 关于在 :attr:`tensors` 中的张量已计算。钩子 将以此梯度作为其参数调用。 钩子不应修改其参数。 此函数返回一个带有方法 `handle.remove()` 的句柄,用于移除钩子。 .. 注意:: 查看::ref:`backward-hooks-execution` 了解有关此钩子何时执行的更多信息 的执行方式以及相对于其他钩子的执行顺序。 示例:: >>> 导入 torch ... >>> a = torch.rand(2, 3, requires_grad=True) >>> b = torch.rand(2, 3, requires_grad=True) >>> c = a * b >>> d = a * b ... >>> 定义函数 fn(grads): ... 打印([g is not None for g in grads]) ... >>> torch.autograd.graph.register_multi_grad_hook((a, b, c, d), fn) ... >>> c.sum().backward(retain_graph=True) [True, True, True, False] >>> c.sum().backward(inputs=(a,), retain_graph=True) [True, False, True, False] ... """ supported_modes = (所有, 任何) 锁定 = 线程.() 如果 模式 支持的模式: 抛出 值错误(f"期望模式为以下之一"{支持的模式}但是得到了{模式}") 如果 模式 == 所有: 数量: 字典[int, int] = {} 调用次数 = 缓冲区: 字典[int, 列表[可选[PyTorch.张量]]] = {} 梯度函数 = 列表(地图(_获取梯度函数或梯度累积器, 张量)) 张量长度 = 长度(张量) def get_inner_hook(索引: int) -> 可调用[[PyTorch.张量] ] def 内部钩子(研究生: PyTorch.张量) -> : 非局部 数量, 调用次数, 缓冲区, fn 标识符 = PyTorch._C._当前图任务 ID() 断言 ( 标识符 != -1 ), "期望这个钩子在反向调用中被调用" 数量[id] = 数量.获取(id, 0) 缓冲区[id] = 缓冲区.获取(id, [] * 张量长度) 替换为 : 当前计数, 数量[id] = 数量[id] 数量[id] + 1 如果 当前计数 == 0: 第一次调用时,计算实际的 nb_calls 和 buffer 调用次数 = 总和( 地图(PyTorch._C._will_engine_execute_node, 梯度函数) ) 缓冲区[id] [索引] = 梯度 断言 调用次数 如果 当前计数 == 调用次数 - 1: fn = 角色(可调用[[序列[可选[PyTorch.张量]], ] 函数) 函数(缓冲区[id]) 删除 数量[id] 删除 缓冲区[id] 返回 内部钩子 处理 = 元组( t.注册钩子(get_inner_hook(i)) for i, t 列举(张量) ) 如果...否则 模式 == 任何: fn = 角色(可调用[[PyTorch.张量] ] 函数) 挂钩: 字典[int, bool] = 默认字典(bool) @functools.包装(函数) def wrapped_fn(研究生: PyTorch.张量) -> : 非局部 运行钩子 标识符 = PyTorch._C._当前图任务 ID() 断言 标识符 != -1, "期望这个钩子在反向调用中被调用" 替换为 : 上一页, 挂钩[id] = 挂钩[id] 真实 如果 上一页: 返回 函数(研究生) 处理 = 元组( 张量.注册钩子(wrapped_fn) for 张量 张量 如果 张量.需要梯度 ) 返回 _MultiHandle(处理) # type: ignore[possibly-undefined]
# 允许对保存用于反向传播的张量进行修改 # # 1. 张量被保存用于反向传播 # - 记录 Python 对象 ID 和张量的版本 # - 记录别名信息(基类数据指针+版本) # - 保存原始数据,以便我们控制其生命周期 # 2. 任何时间张量进行原地修改 对于每个被它别名的张量: 使用其对象 ID 和版本检查是否已保存 如果已保存,则克隆它 删除对原始对象的引用 # 3. 在反向过程中 # - 如果克隆存在,张量必须在原地被修改 _允许在保存的张量上启用修改: 布尔值 = _TID: 类型别名 = 元组[int, int, int] _SID: 类型别名 = 元组[int, int] def _get_tid(张量: PyTorch.张量) -> _TID: # 错误:这几乎肯定是一个错误。 如果 isinstance( 张量, ( PyTorch.子类.fake_tensor.假 Tensor, PyTorch.子类.functional_tensor.FunctionalTensor, ), ): 数据指针 = 0 否则: 数据指针 = 张量.数据指针() 返回 (id(张量), 数据指针, 张量._版本) def 获取会话 ID(张量: PyTorch.张量) -> _SID: # 错误:这几乎肯定是一个错误。 如果 isinstance( 张量, ( PyTorch.子类.fake_tensor.假 Tensor, PyTorch.子类.functional_tensor.FunctionalTensor, ), ): 数据指针 = 0 否则: 数据指针 = 张量.数据指针() 返回 (数据指针, 张量._版本) 处理: 通过 _与克隆体交换(保存张量钩子): def __init__(self, ctx: "_允许在保存的上下文中进行突变") -> : def 打包钩子(张量: PyTorch.张量) -> 处理: tid = _get_tid(张量) sid = 获取会话 ID(张量) # Tensors 保存用于反向传播的条目在 _tid_to_weakhandle 中 处理: 可选[处理] = # 保存别名信息 ctx.sid_to_tid[sid].添加(tid) # NB: 同一个版本的同种张量可以被保存多次 如果 tid ctx.tid_to_weakhandle: handle = 处理() ctx.tid_to_weakhandle[tid] = handle ctx.原始[处理] = 张量 否则: 存储对句柄的额外强引用 handle = ctx.tid_to_weakhandle[tid] 返回 handle def 解包钩子(处理: 处理) -> PyTorch.张量: 错误信息 = ( "尝试在 'allow_mutation_on_saved_tensors' 上下文之外回溯" "其中图最初被记录的上下文中。" ) 断言 _允许在保存的张量上启用修改, 错误信息 如果 handle ctx.克隆的: res = ctx.克隆的[处理] 否则: 断言 handle ctx.原始, 错误信息 res = ctx.原始[处理] 返回 res 超级().__init__(打包钩子, 解包钩子) _CloneArgBeforeMutateMode(火炬调度模式): def __init__(self, ctx: "_允许在保存的上下文中进行突变") -> : self.ctx = ctx def __torch_dispatch__( self, 函数: "运算符重载", 类型: 迭代器[类型] 参数: 元组[任何, ...] = (), kwargs: 可选[字典[任何, 任何]] = , ) -> 任何: kwargs = kwargs 或者 {} def 可能克隆(t: PyTorch.张量) -> : tid = _get_tid(t) sid = 获取会话 ID(t) ctx = self.ctx 如果 sid ctx.sid_to_tid: for tid ctx.sid_to_tid[sid] 如果 tid ctx.tid_to_weakhandle: 我们知道,如果 tid 在 sid_to_tid 中,那么它也必须在 # tid_to_weakhandle。然而,张量可能是 # 保存过一次,但在修改之前被回退清除了 就地。考虑以下示例: # # >>> a = torch.randn(2, 3, requires_grad=True).clone() # >>> out = (a**2).sum() # >>> out.backward() # >>> a.sin_() continue handle = ctx.tid_to_weakhandle[tid] 如果 handle ctx.克隆的: # 同样的精确张量已经被克隆过了 continue ctx.克隆的[处理] = ctx.原始[处理].克隆() 删除 ctx.原始[处理] for 索引, 参数 列举(函数._模式.参数): 如果 参数.别名信息 参数.别名信息.是否写入: 如果 参数.是否输出: 可能克隆(kwargs["out"]) 如果...否则 isinstance(参数[索引] 列表): # 每个案例。(可能的优化:如果大多数张量需要克隆,使用 for each clone?) # 如果需要克隆张量,使用 for each clone? for t 参数[索引] 可能克隆(t) 否则: 可能克隆(参数[索引]) 返回 函数(*参数, **kwargs) _允许在保存的上下文中进行突变: def __init__(self) -> : self.克隆的: 可变映射[处理, PyTorch.张量] = WeakKeyDictionary() self.原始: 可变映射[处理, PyTorch.张量] = WeakKeyDictionary() self.tid_to_weakhandle: 可变映射[_TID, 处理] = 弱值字典() self.sid_to_tid: 字典[_SID, 设置[_TID]] = 默认字典(设置) def 清晰(self) -> : self.克隆的.清晰() self.原始.清晰() self.tid_to_weakhandle.清晰() self.sid_to_tid.清晰()
[文档]@contextlib.contextmanager def allow_mutation_on_saved_tensors() -> ( 生成器[_AllowMutationOnSavedContext, None, None] ): 在反向传播过程中允许保存可变张量的上下文管理器。 在此上下文管理器中,为反向传播保存的张量在变异时被克隆 因此,原始版本在回溯时仍然可以使用。通常,在回溯时修改张量 保存用于反向操作的内容,在反向操作中使用时将引发错误。 确保正确行为,正向和反向都应该在相同的上下文管理器下运行 在同一个上下文管理器下。 返回: 一个存储此上下文管理器所管理状态的 _AllowMutationOnSavedContext 对象。 环境管理器。此对象在调试过程中可能很有用。 环境管理器管理的状态在退出时自动清除。 示例:: >>> 导入 torch >>> with torch.autograd.graph.allow_mutation_on_saved_tensors(): ... # forward ... a = torch.ones(2, 3, requires_grad=True) ... b = a.clone() ... out = (b**2).sum() ... b.sin_() ... # backward ... out.sum().backward() ... tensor([[0.8415, 0.8415, 0.8415], [0.8415, 0.8415, 0.8415]], grad_fn=) """ global _allow_mutation_on_saved_tensors_enabled ctx = _AllowMutationOnSavedContext() with _swap_with_cloned(ctx), _CloneArgBeforeMutateMode(ctx): try: if _allow_mutation_on_saved_tensors_enabled: raise RuntimeError( "允许在已保存张量上执行突变的环境不能嵌套" ) _允许在保存的 tensor 上启用突变 = True yield ctx finally: ctx.clear() _allow_mutation_on_saved_tensors_enabled = False
def _register_logging_hooks_on_whole_graph( t_outputs: 序列
[联合[PyTorch.张量, 梯度边缘]], ) -> 可调用[] ] 梯度函数 = 列表(地图(_获取梯度函数或梯度累积器, t_outputs)) def 迭代图(: 列表[节点]) -> 迭代器[节点] 如果 : 返回 已见: 设置[节点] = 设置() q: 双端队列[节点] = 双端队列() for 节点 : 如果 节点 : 已见.添加(节点) q.添加(节点) q: 节点 = q.popleft() for 函数, _ 节点.下一个函数: 如果 fn 看过 或者 fn : continue 已见.添加(函数) q.添加(函数) 产生 节点 def 格式化(t: 可选[PyTorch.张量]) -> str: 避免循环导入 来自 torch.testing._internal.common_utils 导入 dtype_abbrs 如果 t : 返回 "None" 返回 f"{数据类型缩写[t.数据类型]}[{“,”.连接(地图(str, t.形状))}]" def 预钩子(grad_outputs: 序列[可选[PyTorch.张量]]) -> : 节点 = PyTorch._C.当前自动微分节点() 梯度输出字符串 = f"[{逗号.连接(格式化(t) for t grad_outputs)}]" 日志字符串 = f"执行:"{节点}使用 grad_outputs:{grad_outputs_str}" 日志.调试(log_str) 处理 = [节点.注册预钩子(预钩子) for 节点 迭代图(梯度函数)] def 取消注册钩子() -> : for handle 处理: 处理.删除() 返回 注销钩子 def _engine_run_backward( t_outputs: 序列[联合[PyTorch.张量, 梯度边缘]], *参数: 任何, **kwargs: 任何, ) -> 元组[PyTorch.张量, ...] 添加日志钩子 = 日志.获取有效级别() <= 记录.调试 如果 添加日志钩子: 注销钩子 = _register_logging_hooks_on_whole_graph(t_outputs) 尝试: 返回 变量._execution_engine.向后运行( 调用 C++引擎执行反向传播 t_outputs, *参数, **kwargs ) 调用 C++引擎执行反向传播 最后: 如果 添加日志钩子: 取消注册钩子() # type: ignore[possibly-undefined]

© 版权所有 PyTorch 贡献者。

使用 Sphinx 构建,并使用 Read the Docs 提供的主题。

文档

查看 PyTorch 的全面开发者文档

查看文档

教程

深入了解初学者和高级开发者的教程

查看教程

资源

查找开发资源,获取您的疑问解答

查看资源