# mypy: 允许未类型化定义
导入 gc
导入
打字
导入
火炬
来自 .._utils
导入 _dummy_type
如果
不是
有属性(torch._C, "_CudaStreamBase"):
定义虚拟基类
torch._C.字典[
_CUDAGraph] = _dummy_type(
_CUDAGraph)
torch._C.字典["_graph_pool_handle"] = _dummy_type("_graph_pool_handle")
torch._C.字典["_cuda_isCurrentStreamCapturing"] = _dummy_type(
"_cuda_isCurrentStreamCapturing"
)
来自 torch._C
导入 ( # noqa: F401
_cuda_isCurrentStreamCapturing,
_CUDAGraph,
_graph_pool_handle,
)
[文档]def is_current_stream_capturing():
r"""正在当前 CUDA 流上执行 CUDA 图捕获时返回 True,否则返回 False。
如果当前设备上不存在 CUDA 上下文,则返回 False 而不初始化上下文。
"""
返回 _cuda_isCurrentStreamCapturing()
Python shim 有助于 Sphinx 更可靠地处理文档字符串。
[文档]def graph_pool_handle():
返回表示图内存池 ID 的不可见令牌。
查看 :ref:`图内存管理`。
.. 警告::
该 API 处于测试阶段,未来版本中可能会有所变动。
"""
返回_graph_pool_handle()
Python shim 有助于 Sphinx 更可靠地处理文档字符串。
[文档]
类 CUDAGraph(torch._C._CUDAGraph):
rCUDA 图包装器。
.. 警告::
此 API 处于测试版,未来版本中可能会有所变化。
"沉浸式翻译"
def __new__(类):
返回
超级().__new__(
类)
[文档] def capture_begin(self, pool=None, capture_error_mode="global"):
开始在当前流上捕获 CUDA 工作。
通常情况下,您不应该自己调用 `capture_begin`。
使用 :class:`~torch.cuda.graph` 或 :func:`~torch.cuda.make_graphed_callables`,
它们内部会调用 `capture_begin`。
参数:
池(可选):令牌(由 :func:`~torch.cuda.graph_pool_handle` 或
meth:`other_Graph_instance.pool()` 返回),
暗示此图可能与指定的池共享内存。 详见 :ref:`图内存管理`。
capture_error_mode (str, optional): 指定图捕获流的 cudaStreamCaptureMode。
可以是 "global"、"thread_local" 或 "relaxed"。在 CUDA 图捕获期间,一些操作,如 cudaMalloc,
可能不安全。"global"在其他线程中的操作将引发错误,"thread_local"只会对线程本地变量引发错误。
当前线程中的操作,并且“relaxed”对这些操作不会出错。不要更改此设置
除非你熟悉 `cudaStreamCaptureMode `_
""" # noqa: B950
super().capture_begin(pool=pool, capture_error_mode=capture_error_mode)
[文档] def capture_end(self):
结束当前流的 CUDA 图捕获。
在调用`capture_end`之后,可以在此实例上调用`replay`。
通常情况下,您不应该自己调用`capture_end`。
使用`:class:`~torch.cuda.graph`或`:func:`~torch.cuda.make_graphed_callables`。
内部调用 `capture_end`。
"""
super().capture_end()
[文档] def replay(self):
重放此图捕获的 CUDA 工作。
super().重放()
[文档] def 重置(self):
删除此实例当前持有的图形。
super().reset()
[文档] def pool(self):
r"""返回表示此图内存池 id 的不可见令牌。
此 id 可以可选地传递给另一个图的 `capture_begin`,
这暗示其他图可能共享相同的内存池。
"""
返回 super().pool()
[文档] def 启用调试模式(self):
启用 CUDAGraph.debug_dump 的调试模式。
返回 super().enable_debug_mode()。
[文档] def debug_dump(self, debug_path):
"
参数:
debug_path(必需):导出图的路径。
调用调试函数以导出图,如果通过 CUDAGraph.enable_debug_mode()启用了调试。
调用调试函数以导出图,如果通过 CUDAGraph.enable_debug_mode()启用了调试。
"""
返回调用基类的 debug_dump 方法,并将 debug_path 作为参数
[文档]
类 graph:
r捕获 CUDA 工作到 :class:`torch.cuda.CUDAGraph` 对象中,以便稍后回放。有关 CUDA 图的介绍,请参阅 :ref:`CUDA 图语义 `
有关 CUDA 图的概述,请参阅 :ref:`CUDA 图语义 `
详细使用说明和约束条件。
参数:
cuda_graph (torch.cuda.CUDAGraph):用于捕获的图对象。
pool(可选):由调用 :func:`~torch.cuda.graph_pool_handle()` 返回的不可见令牌(
`other_Graph_instance.pool()()` 暗示此图的捕获
可能从指定的池中共享内存。参见 :ref:`图内存管理`。
流 (torch.cuda.Stream, 可选): 如果提供,将设置为上下文中的当前流。
如果未提供,`graph` 将将其内部侧流设置为上下文中的当前流。
capture_error_mode (str, 可选): 指定图捕获流的 cudaStreamCaptureMode。
可以为 "global","thread_local" 或 "relaxed"。在 cuda 图捕获期间,某些操作,如 cudaMalloc,
可能是不安全的。"global" 将在其他线程中的操作上产生错误,"thread_local" 只会在当前线程中的操作上产生错误,
"relaxed" 不会在操作上产生错误。请勿更改此设置
除非您熟悉 `cudaStreamCaptureMode `_
.. 注意::
为了有效共享内存,如果您传递了之前捕获使用的 `pool`,并且之前的捕获使用了显式的 `stream` 参数,那么您应该将相同的 `stream` 参数传递给这次捕获。
此 API 处于测试版,未来版本中可能会有所变化。
.. 警告::
此 API 处于测试版,未来版本中可能会有所变化。
cudaStreamCaptureMode
https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__STREAM.html#group__CUDART__STREAM_1g9d0535d93a214cbf126835257b16ba85
"沉浸式翻译"
# 无需注意:B950
默认捕获流:
输入法.
可选[
torch.cuda.Stream] =
无
def __init__(
self,
cuda_graph,
泳池=
无,
流=
无,
捕获错误模式:
字符串 =
全局,
):
# 懒加载默认捕获流有助于避免循环导入错误。
# 不是线程安全的,但图已经具有一般的(明确记录的)
过程中只能同时进行一个捕获。
如果 self.
类.
默认捕获流
是
无:
self.类.
默认捕获流 = torch.cuda.
流()
self.泳池 = ()
如果
泳池
是
无
否则 (
泳池,)
self.捕获流 = (
流
如果
流
是
不是
无
否则 self.
类.
默认捕获流
)
断言 self.
捕获流
是
不是
无
self.流上下文 = torch.cuda.
流(self.
捕获流)
self.CUDA 图 =
CUDA 图
self.捕获错误模式 =
捕获错误模式
def __进入__(self):
尽可能释放图所需的内存
torch.cuda.同步()
垃圾回收.
收集()
torch.cuda.清空缓存()
# Stackoverflow 似乎对此模式很自在
# https://stackoverflow.com/questions/26635684/calling-enter-and-exit-manually#39172487
self.流上下文.
__进入__()
self.cuda_graph.捕获开始(
*self.泳池,
捕获错误模式=self.
捕获错误模式
)
def __退出__(self,
异常类型, exc_value,
跟踪回溯):
self.cuda_graph.捕获结束()
self.流上下文.
__退出__(
异常类型, exc_value,
跟踪回溯)
返回 None 应传播来自 capture_end 或 stream_ctx.__exit__()的异常
[文档]def
生成图形化可调用对象(
可调用对象,
示例参数,
预热迭代次数=3,
允许未使用输入=
错误,
泳池=
无
):
r接受可调用对象(函数或:class:`nn.Module`对象)并返回图化版本。
每个图化可调用对象的正向传播运行其源可调用对象的过程。
将 CUDA 工作作为 CUDA 图在单个 autograd 节点内执行。
图形化的可调用对象的正向传递还会向 autograd 图追加。
在反向传播过程中,此节点将可调用对象的反向工作作为 CUDA 图执行。
将可调用对象的反向工作作为 CUDA 图执行。
因此,每个图表化的可调用函数都应该是其源可调用函数的直接替换。
在启用自动求导的训练循环中。
请参阅:ref:`部分网络捕获`以获取详细使用和限制。
如果你传递一个包含多个可调用函数的元组,它们的捕获将使用相同的内存池。
请参阅::ref:`图内存管理`以了解何时适用。
参数:
可调用对象(torch.nn.Module 或 Python 函数,或这些对象的元组):要图化的可调用对象或可调用对象列表。
请参阅::ref:`图内存管理`以了解何时传递可调用对象的元组。
是合适的。如果传递一个可调用对象的元组,它们的顺序必须与元组中的顺序相同
它们将在实时工作负载中运行。
sample_args(张量元组或张量元组的元组):为每个可调用对象提供的样本参数。
如果传递了一个单独的可调用对象,则`sample_args`必须是一个包含参数张量的单个元组。
如果传递了一个可调用对象的元组,则 `sample_args` 必须是元组的元组,其中包含类型为 Tensors 的参数张量。
num_warmup_iters (int):预热迭代次数。目前,`DataDistributedParallel` 需要
11 次预热迭代。默认:`3`。
allow_unused_input (bool):如果为 False,则指定在计算输出时未使用的输入
(因此其梯度始终为零)是一个错误。默认为 False。
池(可选):令牌(由 :func:`~torch.cuda.graph_pool_handle` 返回或
meth:`other_Graph_instance.pool()`) 暗示此图可能与指定的池共享内存
。请参阅 :ref:`图内存管理`。
.. 注意::
每个 `sample_args` 中的 Tensor 的 `requires_grad` 状态必须匹配
这是训练循环中对应真实输入的预期情况。
.. 警告::
此 API 处于测试版,未来版本中可能会有所变化。
.. 警告::
每个可调用对象的 `sample_args` 必须只包含张量。不允许其他类型。
.. 警告::
返回的可调用对象不支持高阶微分(例如,双重反向)。
.. 警告::
在传递给 :func:`~make_graphed_callables` 的任何 :class:`~torch.nn.Module` 中,只有参数
可训练。缓冲区必须具有 ``requires_grad=False``。
.. 警告::
在通过 :class:`torch.nn.Module` 通过 :func:`~make_graphed_callables` 后,
您不能添加或删除该模块的任何参数或缓冲区。
.. 警告::
传递给 :func:`~torch.cuda.make_graphed_callables` 的 :class:`torch.nn.Module` 不能有模块钩子
在它们传递时注册。然而,在传递模块后 *注册* 插件
通过::func:`~torch.cuda.make_graphed_callables` 是允许的。
.. 警告::
运行图形可调用函数时,必须以相同的顺序和格式传递其参数
他们出现在那个可调用的`sample_args`中。
.. 警告::
自动混合精度仅在禁用缓存的情况下支持于:func:`~torch.cuda.make_graphed_callables`
。上下文管理器`torch.cuda.amp.autocast()`必须设置`cache_enabled=False`
"沉浸式翻译"
如果 torch.is_autocast_enabled()
和 torch.is_autocast_cache_enabled():
抛出
运行时错误(
"make_graphed_callables 不支持自动缓存。请设置 `cache_enabled=False`。"
)
只有一个可调用对象 =
假
如果
不是 isinstance(
可调用对象,
元组):
只有一个可调用对象 =
真实
可调用对象 = (
可调用对象,)
样本参数 = (
示例参数,)
展平样本参数 =
输入文本为空,请提供需要翻译的文本
for c, args 在 zip(
可调用对象,
示例参数):
如果 isinstance(c, torch.
神经网络.
模块):
断言 (
长度(c._backward_hooks) == 0
和
长度(c.
_前向钩子) == 0
和
长度(c.
_前向预处理钩子) == 0
), (
模块在传递时不得注册钩子。然而,注册钩子
+ 在通过 make_graphed_callables 处理后允许在模块上进行操作。
)
断言
所有(b.requires_grad
是
假 for b
在 c.
缓冲区()), (
在任何`:class:`~torch.nn.Module`传递给`
+ ":func:`~make_graphed_callables`,只有参数可以是可训练的。所有缓冲区都必须有“
+ "``requires_grad=False``。"
)
flatten_arg = torch.工具._pytree.arg_tree_leaves(*
参数)
展平样本参数.append(
元组(
展平参数))
断言
所有(isinstance(arg, torch.
张量) for
参数
在
展平参数), (
在测试版 API 中,sample_args
+ "每个可调用对象必须只包含张量。不允许其他类型。"
)
# 如果一个可调用对象是一个 nn.Module,其完整输入表面是用户显式传递给 forward 的 args(即其 sample_args)以及模块的参数属性。
# 每个可调用对象的长度用户 args
per_callable_len_user_args = [长度(
参数) for args
在
展平样本参数]
每个可调用模块的参数 = [
元组(c.
参数())
如果 isinstance(c, torch.
神经网络.
模块)
否则 ()
for c 在
可调用对象
]
每个可调用对象的静态输入表面 = [
展平样本参数[i] +
可调用模块参数[i]
for i 在
范围(
长度(
可调用对象))
]
前向图 = [torch.cuda.CUDAGraph() for _
在
范围(
长度(
可调用对象))]
反向图 = [torch.cuda.CUDAGraph() for _
在
范围(
长度(
可调用对象))]
内存池 =
图池句柄()
如果
泳池
是
无
否则
泳池
# 预热
# 希望能防止 cudnn 基准测试和其他懒加载的 cuda 工作
确保不会落入任何捕捉中。
torch.cuda.同步()
与 torch.cuda.
流(torch.cuda.
流()):
for 函数,
参数,
静态输入表面
在 zip(
可调用对象,
示例参数,
每个可调用对象的静态输入表面
):
梯度输入,
输出,
输出_grad =
无,
无,
无
for _ 在
范围(
预热迭代次数):
输出 = torch.
工具._pytree.
树叶(
函数(*
参数))
输出_grad =
元组(o for o
在
输出
如果 o.
需要梯度)
如果
长度(
输出梯度) > 0:
grad_inputs = torch.自动微分.
研究生(
输出=
输出梯度,
输入=
元组(
i for i 在
静态输入表面
如果 i.requires_grad
),
梯度输出=
元组(
torch.空值类似(o) for o
在
输出
如果 o.requires_grad
),
仅输入=
是,
允许未使用=
允许未使用输入,
)
for v 在 [
输出,
输出梯度,
梯度输入
]
删除 v
torch.cuda.同步()
所有捕获在此共享一个内存池。为了避免重放互相破坏对方的内存。
最安全的做法是按照它们运行的顺序捕获所有遍历:
# 前进 1,前进 2,... 前进 N,然后后退 N,后退 N-1,... 后退 1。
捕获前向图
每个可调用静态输出 =
输入文本为空,请提供需要翻译的文本
每个可调用输出未展开规范 =
输入文本为空,请提供需要翻译的文本
for 函数,
参数,
前向图
在 zip(
可调用对象,
示例参数,
前向图):
与 torch.cuda.graph(
前向图,
泳池=
内存池):
输出 =
函数(*
参数)
展平输出,
规范 = torch.
工具._pytree.
树形展平(
输出)
每可调用静态输出.append(
元组(
展平输出))
每个可调用输出的反展平规范.append(
规格)
# 逆序捕获反向图
每个可调用静态梯度输出 =
输入文本为空,请提供需要翻译的文本
每个可调用静态梯度输入 =
输入文本为空,请提供需要翻译的文本
for static_input_surface, static_outputs, 反向图
在 zip(
反转(
每可调用静态输入表面),
反转(
每可调用静态输出),
反转(
反向图集),
):
# 假设所有静态输出都要求梯度
# 断言所有(static_outputs).requires_grad,"图调用函数的输出必须要求梯度。"
static_grad_outputs = 元组(
torch.空值类似(o)
如果 o.requires_grad
否则
无 for o
在
静态输出
)
输出_grad =
元组(o for o
在
静态输出
如果 o.
需要梯度)
grad_inputs = 无
如果
长度(
输出梯度) > 0:
与 torch.cuda.graph(
逆图,
泳池=
内存池):
grad_inputs = torch.自动微分.
研究生(
输出=
输出梯度,
输入=
元组(i for i
在
静态输入表面
如果 i.
需要梯度),
梯度输出=
元组(o for o
在 static_grad_outputs
如果 o
是
不是
无),
仅输入=
是,
允许未使用=
允许未使用输入,
)
# 构建适合从 Graphed.backward 返回的元组:
# 使用 None 填充实际需要的梯度,以在不需要梯度的输入的梯度槽中填充。
我实在想不出这个模式的俏皮一句话。
静态梯度输入 =
输入文本为空,请提供需要翻译的文本
梯度索引 = 0
for 参数
在 static_input_surface:
如果 arg.requires_grad
和 grad_inputs
是
不是
无:
静态梯度输入.append(
梯度输入[grad_idx])
梯度索引 += 1
否则:
静态梯度输入.append(
无) # type: ignore[arg-type]
静态梯度输入 =
元组(
静态梯度输入)
# 类型:忽略[赋值]
每个可调用静态梯度输出.append(
静态梯度输出)
每个可调用静态梯度输入.append(
静态梯度输入)
# 反转最近的两个列表
每个可调用静态梯度输出.reverse()
每个可调用静态梯度输入.reverse()
# 现在,对于每个可调用列表,per_callable_*[i] 存储第 i 个可调用的内容
def 创建图形化的自动微分函数(
前向图,
逆图,
模块参数,
len_user_args,
output_unflatten_spec,
static_input_surface,
static_outputs,
静态梯度输出,
静态梯度输入,
):
类
图形化(torch.
自动微分.
函数):
@staticmethod
def 前向(ctx, *
输入):
在这个阶段,只有用户参数可能(可能)是新的张量。
for i 在
范围(len_user_args):
如果 static_input_surface[i].
数据指针() !=
输入[i].
数据指针():
static_input_surface[i].复制_(
输入[i])
前向图.
回放()
断言 isinstance(static_outputs,
元组)
返回
元组(o.detach() for o
在 static_outputs)
@staticmethod
@torch.自动微分.
函数.
一次可微
def 反向(ctx, *
梯度):
断言
长度(
梯度) ==
长度(
静态梯度输出)
for g, 梯度
在 zip(
静态梯度输出,
梯度):
如果 g
是
不是
无:
如果自动微分之神仁慈的话,请不要复制以下内容
入学新生已经坐在正确的位置
如果 g.
数据指针() !=
研究生.
数据指针():
g.复制_(
研究生)
逆图.
回放()
# 输入参数不需要梯度期望为 None 梯度。
断言 isinstance(
静态梯度输入,
元组)
返回
元组(
b.detach() 如果 b
是
不是
无
否则 b for b
在
静态梯度输入
)
def 功能化(*
用户参数):
运行与图中的所有可能需要梯度的输入相等的自动微分函数
(显式用户参数 + 模块参数)
假设模块参数自捕获以来没有改变。
展平用户参数 = torch.
工具._pytree.arg_tree_leaves(*
用户参数)
out = 图形化.
应用(*(
元组(
展平用户参数) +
模块参数))
返回 torch.
工具._pytree.
树形展开(
输出, output_unflatten_spec)
返回
函数化
组合最终的图形化可调用对象
返回 =
输入文本为空,请提供需要翻译的文本
for i, 函数
在
列举(
可调用对象):
图形化 =
创建图形化的自动微分函数(
前向图[i
]
反向图集[i
]
可调用模块参数[i
]
每调用长度用户参数[i
]
每个可调用输出的反展平规范[i
]
每可调用静态输入表面[i
]
每可调用静态输出[i
]
每个可调用静态梯度输出[i
]
每个可调用静态梯度输入[i
]
)
如果 isinstance(
函数, torch.
神经网络.
模块):
def 制作图形化前向(
函数,
图训练状态,
图形化,
原始转发):
def 新正向(*
用户参数):
# 如果模块的训练或评估状态与我们所绘制的相匹配,
运行图,否则运行原始前向方法
如果
函数.
训练 ==
图训练状态:
返回
图形化(*
用户参数)
否则:
返回
原始转发(*
用户参数)
返回
新的转发
函数.
前向传播 =
制作图形化前向(
函数,
函数.
训练,
图形化,
函数.
前向)
# 类型:忽略[赋值]
返回.append(
函数)
否则:
返回.append(
图形化)
如果
只有一个可调用的:
返回
返回[0]
返回
元组(
返回)