• 文档 >
  • 模块代码 >
  • torch >
  • torch.fx.subgraph_rewriter
快捷键

torch.fx.subgraph_rewriter 源代码

导入 复制
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, 替换) 返回 匹配和替换

© 版权所有 PyTorch 贡献者。

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

文档

查看 PyTorch 的全面开发者文档

查看文档

教程

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

查看教程

资源

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

查看资源