导入
复制
from dataclasses 导入
数据类
from 打字
导入
任何,
可调用,
命名元组,
可选,
类型检查,
联合
导入
火炬
from 兼容性
导入
兼容性
from 符号追踪
导入
符号化跟踪
from .graph 导入
图
from .图模块
导入
图模块
from .节点
导入
节点
如果
类型检查:
from .passes.utils.matcher_with_name_node_map_utils 导入
内部匹配
全部 = [
匹配,
替换模式,
"用过滤器替换模式",
"替换后的模式",
]
@compatibility(兼容旧版本=
是)
类
匹配(
命名元组):
匹配到的节点
锚点:
节点
# 将模式子图中的节点映射到更大图中
节点映射:
字典[
节点,
节点]
@compatibility(兼容旧版本=
错误)
@dataclass
类
替换模式:
# 匹配的节点来源
锚点:
节点
# 将模式子图中的节点映射到更大图中
节点映射:
字典[
节点,
节点]
# 列出添加到图中的节点
替换列表:
列表[
节点]
def 替换属性(gm: GraphModule,
替换:
火炬.
神经网络.
模块)
翻译
无:
gm.删除所有未使用的子模块()
如果 isinstance(
替换, GraphModule):
替换.graph.
检查()
def 尝试获取属性(gm:
火炬.
神经网络.
模块,
目标:
字符串)
翻译
可选[
任何
]
模块路径, _,
属性名称 =
目标.
分割(
“。”)
尝试:
模块:
火炬.
神经网络.
模块 = gm.
获取子模块(
模块路径)
除了
属性错误:
返回
无
属性 = getattr(
模块,
属性名称,
无)
返回
属性
为
节点
在 gm.graph.
节点:
如果
节点.
操作符 ==
"调用模块"
或者
节点.
操作符 ==
获取属性:
属性 =
尝试获取属性(gm,
节点.
目标)
替换属性 = try_get_attr(
替换,
节点.
目标)
# CASE 1: 此目标已作为属性存在于我们的
# 结果 GraphModule 中。无论它是否存在于
# `replacement`中,现有的子模块优先。
如果 gm_attr is
不是
无:
continue
# CASE 2: 目标作为 `replacement` 中的一个属性存在
# 仅此,因此我们需要将其复制过来。
如果...否则 replacement_attr is
不是
无:
新属性 =
复制.
深拷贝(
替换属性)
如果 isinstance(
替换属性,
火炬.
神经网络.
模块):
gm.添加子模块(
节点.
目标,
新属性)
else:
setattr(gm, 节点.
目标,
新属性)
# CASE 3:目标在 `gm` 中不存在作为属性
# 或者 `replacement`
else:
提升
运行时错误(
尝试在子图重写期间创建一个 "',
节点.
操作,
'" 节点
f的目标{
节点.
目标}
,但 "
"引用的属性不存在于替换的 GraphModule 中"
"存在",
)
gm.graph.检查()
[文档]@compatibility(
兼容旧版本=
是)
def 替换模式(
gm: GraphModule,
模式:
联合[
可调用, GraphModule
]
替换:
联合[
可调用, GraphModule
]
) 翻译
列表[
匹配
]
""
匹配所有可能的非重叠操作符及其
图模块的图中数据依赖(`pattern`)
(``gm``),然后将这些匹配的子图替换为另一个
子图(`replacement`)。
参数:
``gm``: 包装图以进行操作的 GraphModule
``pattern``: 在 ``gm`` 中匹配以进行替换的子图
``replacement``: 用以替换 ``pattern`` 的子图
返回:
List[Match]: 代表匹配位置的 ``Match`` 对象列表
在原始图中匹配到 ``pattern`` 的地方。列表为空,如果没有匹配项。
如果没有匹配项,则为空。``Match`` 定义为:
.. 代码块 :: python
class Match(NamedTuple):
# 节点来源
anchor: 节点
# 将模式子图中的节点映射到更大图中的节点
nodes_map: Dict[Node, Node]
示例:
.. 代码块 :: python
导入 torch
从 torch.fx 导入 symbolic_trace 和 subgraph_rewriter
class M(torch.nn.Module):
def __init__(self) -> None:
super().__init__()
def forward(self, x, w1, w2):
m1 = torch.cat([w1, w2]).sum()
m2 = torch.cat([w1, w2]).sum()
return x + torch.max(m1) + torch.max(m2)
def pattern(w1, w2):
return torch.cat([w1, w2])
def replacement(w1, w2):
return torch.stack([w1, w2])
traced_module = symbolic_trace(M())
subgraph_rewriter 替换模式(traced_module, 模式, 替换)
上述代码将首先匹配“forward”中的“pattern”
``traced_module``的方法。模式匹配基于
使用定义关系,而不是节点名称。例如,如果您有
在 `pattern` 中,你可以匹配 `p = torch.cat([a, b])`
在原始 `forward` 函数中,`m = torch.cat([a, b])`
尽管变量名不同(`p` 与 `m`)
`pattern` 中的 `return` 语句是根据其内容进行匹配的
仅包含值;它可能或可能不与“返回”语句匹配
换句话说,模式不必扩展到更大图的全长
当模式匹配时,它将从更大图中移除
当模式匹配时,它将从更大图中移除
函数中的“pattern”将被“replacement”替换。如果函数中有多个匹配项,则每个非重叠匹配项将被替换。在匹配重叠的情况下,重叠匹配项集中找到的第一个匹配项将被替换。
将替换。在匹配重叠的情况下,重叠匹配项集中找到的第一个匹配项将被替换。
将被替换。在匹配重叠的情况下,重叠匹配项集中找到的第一个匹配项将被替换。
将被替换。在匹配重叠的情况下,重叠匹配项集中找到的第一个匹配项将被替换。
("First"这里定义为拓扑排序中的第一个)
节点使用-定义关系。在大多数情况下,第一个节点
是直接出现在 ``self`` 后面的参数,而
最后节点是函数返回的内容。
注意一点很重要,那就是必须在使用 Callable 时使用``pattern``参数,并且``replacement``Callable 的参数必须与模式匹配。
Callable 必须在其自身中使用,并且``replacement``Callable 的参数必须与模式匹配。
第一条规则是为什么在上面的代码块中,为什么必须使用 Callable 的参数。
规则。第一条规则是为什么在上面的代码块中,为什么必须使用 Callable 的参数。
``forward`` 函数有参数 ``x, w1, w2``,但
``pattern`` 函数只有参数 ``w1, w2``。``pattern``
函数不使用 ``x``,因此不应将 ``x`` 作为参数指定。
作为第二条规则的例子,考虑将
.. 代码块 :: python
def pattern(x, y):
return torch.neg(x) + torch.relu(y)
的
.. 代码块 :: python
def replacement(x, y):
return torch.relu(x)
在这种情况下,`replacement` 需要和 `pattern`(`x` 和 `y`)相同的参数数量
即使 `y` 在 `replacement` 中没有被使用
。
在调用 `subgraph_rewriter.replace_pattern` 之后,生成的
Python 代码看起来是这样的:
.. 代码块 :: python
def forward(self, x, w1, w2):
stack_1 = torch.stack([w1, w2])
sum_1 = stack_1.sum()
stack_2 = torch.stack([w1, w2])
sum_2 = stack_2.sum()
max_1 = torch.max(sum_1)
add_1 = x + max_1
max_2 = torch.max(sum_2)
add_2 = add_1 + max_2
return add_2
""
match_and_replacements = 替换模式(gm,
模式,
替换)
返回 [
匹配(
锚点=m.
锚点,
节点映射=m.
节点映射)
为 m
在
匹配与替换
]
# 实验性 API,不向后兼容
@compatibility(兼容旧版本=
错误)
def 使用过滤器替换模式(
gm: GraphModule,
模式:
联合[
可调用,
图, GraphModule
]
替换:
联合[
可调用,
图, GraphModule,
无] =
无,
匹配过滤器:
可选[
列表[
可调用[[
内部匹配,
图,
图
]
布尔]]
] = 无,
忽略字面值:
布尔值 =
错误,
# 放在末尾以避免破坏向后兼容性
替换回调:
可选[
可调用[[
内部匹配,
图,
图
]
图]
] = 无,
) 翻译
列表[
替换模式
]
""
查看 replace_pattern 以获取文档。此函数是一个带有额外 match_filter 参数的重载。
参数:
``match_filters``: 函数列表,这些函数接收
(match: InternalMatch, original_graph: Graph, pattern_graph: Graph) 并返回一个布尔值,表示
匹配是否满足条件。
请参阅 matcher_utils.py 以获取 InternalMatch 的定义。
``replacement_callback``: 一个接受匹配项并返回替换图的函数。
要使用的图。这允许您根据匹配项构建替换图。
基于匹配项构建替换图。
""
返回 _replace_pattern(
gm, 模式,
替换,
匹配过滤器,
忽略字面值,
替换回调函数
)
def _替换模式(
gm: GraphModule,
模式:
联合[
可调用,
图, GraphModule
]
替换:
联合[
可调用,
图, GraphModule,
无] =
无,
匹配过滤器:
可选[
列表[
可调用[[
内部匹配,
图,
图
]
布尔]]
] = 无,
忽略字面值:
布尔值 =
错误,
# 放在末尾以避免破坏向后兼容性
替换回调:
可选[
可调用[[
内部匹配,
图,
图
]
图]
] = 无,
) 翻译
列表[
替换模式
]
from torch.fx.passes.utils 匹配工具
导入
内部匹配,
子图匹配器
如果
匹配过滤器 is
无:
匹配过滤器 =
输入文本为空,请提供需要翻译的文本
获取 `gm`、`pattern`、`replacement` 的图表
原始图表:
图 = gm.
图
如果 isinstance(
模式, GraphModule):
模式图表 =
模式.
图
如果...否则 isinstance(
模式,
图):
模式图表 =
模式
else:
模式图 =
符号追踪(
模式).
图
匹配器 =
子图匹配器(
模式图,
匹配输出=
错误,
匹配占位符=
错误,
移除重叠匹配=
是,
忽略字面意思=
忽略字面意思,
)
匹配:
列表[
内部匹配] =
匹配器.
匹配(
原始图)
# 过滤掉不匹配的匹配项
_匹配项 = [
m
为 m
在
匹配
如果
所有(
匹配过滤器(m,
原始图,
模式图)
为
匹配过滤器
在
匹配过滤器列表
)
]
如果 isinstance(
替换, GraphModule):
公共替换图 =
替换.
图
如果...否则 isinstance(
替换,
图):
公共替换图 =
替代方案
如果...否则
可调用(
替换):
常见替换图 =
符号追踪(
替换).
图
else:
断言 (
替换回调 is
不是
无
), "必须提供替换 GraphModule 或替换回调"
常见替换图 =
无
随着我们逐步替换节点,我们需要跟踪匹配结果应该如何变化
匹配变更节点:
字典[
节点,
节点] = {}
匹配与替换 =
输入文本为空,请提供需要翻译的文本
为
匹配
在
匹配项:
如果
替换回调 is
不是
无:
替换图 =
替换回调(
匹配,
原始图,
模式图
)
else:
断言 (
公共替换图 is
不是
无
), "必须提供替换的 GraphModule 或替换的回调函数"
替换图 =
公共替换图
替换占位符 = [
n 为 n
在
替换图.
节点
如果 n.
操作符 ==
占位符
]
# 构建替换图输入与原始图输入生产节点之间的连接
# 初始化 `val_map`,将占位节点在 `replacement` 中的映射
# 与 `original_graph` 中相应的节点对应
断言
长度(
匹配.
占位节点) ==
长度(
替换占位符)
值映射:
字典[
节点,
节点] = {}
为 rn, gn
在 zip(
替换占位符,
匹配.
占位节点):
如果 isinstance(gn,
节点):
值映射[rn] =
匹配变更节点.
获取(
干,
干)
如果
干 !=
值映射[
仁
]
更新 match.placeholder_nodes 和 match.nodes_map 中的 gn 替换的节点
gn_ind = 匹配.placeholder_nodes.
索引(gn)
匹配.
占位节点[gn_ind] =
匹配变更节点[gn]
地图键 =
列表(
匹配.
节点映射.
键
(()))
列表(
匹配.
节点映射.
值()).
索引(gn)
]
匹配.
节点映射[
映射键] =
匹配变更节点[gn]
else:
值映射[rn] = gn
# 复制替换图
用户节点:
设置[
节点] =
设置()
为 n
在
匹配.
返回节点:
用户节点.
更新(n.
用户们)
首个用户节点 =
无
如果
长度(
用户节点) == 0:
首个用户节点 =
无
如果...否则
长度(
用户节点) == 1:
首个用户节点 =
下一(
迭代(
用户节点))
else:
如果存在多个用户节点,我们需要找到第一个用户节点
在当前`original_graph`执行顺序中
为 n
在 original_graph.
节点:
如果 n
在
用户节点:
第一个用户节点 = n
断开
第一个下一节点 =
无
如果
第一个用户节点 is
无:
# 无用户,因此我们在第一个 next 之前插入替换图
# 返回节点的节点
next_node = 无
为 n
在
反转(original_graph.
节点):
如果 n
在
匹配.
返回节点:
第一个下一个节点 =
下一个节点
断开
else:
下一个节点 = n
插入点 = (
第一个用户节点
如果
第一个用户节点 is
不是
无
否则
第一个下一节点
)
断言
插入点 is
不是
无,
"插入点不能为空"
与
原始图.
插入之前(
插入点):
复制返回节点 =
原始图.graph_copy(
替换图,
值映射
)
如果 isinstance(
复制返回的节点,
节点):
复制返回的节点 = (
复制返回的节点,)
获取替换到图中节点的列表
替换节点:
列表[
节点] = [
v 为 v
在
值映射.
值()
如果 v
不是
在
匹配.
占位节点
]
# 将替换子图的输出节点连接到
# 原图正确的位置
断言
长度(
匹配.
返回节点) ==
长度(
复制返回节点) # type: ignore[arg-type]
为 gn,
复制节点
在 zip(
匹配.
返回节点,
复制返回节点): # type: ignore[arg-type]
gn.替换所有引用(
复制节点)
节点匹配更改[gn] =
复制节点
# 删除原始节点
为
节点
在
反转(
模式图.
节点):
如果
节点.
操作符 !=
占位符
和
节点.
操作符 !=
输出:
gn = 匹配.
节点映射[
节点]
gm.graph.删除节点(gn)
匹配与替换.append(
替换模式(
锚点=
匹配.
锚点[0
]
节点映射=
匹配.
节点映射,
替换=
替换节点,
)
)
# 更新传入的 GraphModule 以反映新的状态
`original_graph`
gm.重新编译()
如果 `replacement` 是一个 nn.Module,我们需要确保
所有子模块都已正确复制
如果 isinstance(
替换,
火炬.
神经网络.
模块):
_替换属性(gm,
替换)
返回
匹配和替换