virtualGraph 和 pipelineStage Graphcore 的 PopART/Poplar 库之间的区别
Difference between virtualGraph and pipelineStage Graphcore's PopART/Poplar libraries
我正在尝试使用 Graphcore 的 PopART framework(Poplar API 的一部分)来实现一个基本的流水线模型,以加速我的模型,该模型分布在多个处理器上。
我正在关注他们的 example code, but I notice the example does not use the pipelineStage()
call, which is used in some of their other applications (namely Bert),而是使用 virtualGraph()
来定义处理器,操作应该 运行。
下面示例的一小段:
# Dense 1
W0 = builder.addInitializedInputTensor(
init_weights(num_features, 512))
b0 = builder.addInitializedInputTensor(init_biases(512))
with builder.virtualGraph(0):
x1 = builder.aiOnnx.gemm([x0, W0, b0], debugPrefix="gemm_x1")
x2 = builder.aiOnnx.relu([x1], debugPrefix="relu_x2")
# Dense 2
W1 = builder.addInitializedInputTensor(init_weights(512, num_classes))
b1 = builder.addInitializedInputTensor(init_biases(num_classes))
with builder.virtualGraph(1):
x3 = builder.aiOnnx.gemm([x2, W1, b1], debugPrefix="gemm_x3")
x4 = builder.aiOnnx.relu([x3], debugPrefix="relu_x4")
相反,Bert 示例似乎创建了一个结合了 virtualGraph()
和 pipelineStage()
的上下文:
self.stack.enter_context(self.builder.pipelineStage(self.pipelineStage))
我不确定哪种风格应该是首选。仅使用 virtualGraph()
有什么影响吗?
virtualGraph
和 pipelineStage
是 Graphcore PopART 框架中的两个不同概念,尽管它们也相关。
virtualGraph
(参见您提到的Setting the IPU number for operations section of the PopART User Guide and the PopART C++ API for reference) enables splitting a graph into multiple parts, to run on multiple IPUs. Using virtualGraph
on its own, as shown in the code example,表示运行在模型的分配部分上顺序排列。
另一方面,pipelineStage
允许您将图形分成几个阶段,如果可能的话,可以 运行 在不同的 IPU 上并行。您可以灵活地选择应该在每个流水线阶段放置哪些操作。流水线由选项 opts.enablePipelining
启用(参见 PopART C++ API)。
默认情况下,流水线隐式地为每个 virtualGraph
创建一个 pipelineStage
。
但是,可以为单个 virtualGraph
指定多个 pipelineStage
。当模型的两个部分共享相同的大型数据集时,这可能很有用。这就是 BERT 模型中使用 pipelineStage
和 virtualGraph
组合的原因:BERT 有一个很大的嵌入矩阵,用于模型的开始和结束。可以将两个操作放在同一个 virtualGraph
中以位于同一个 IPU 上,这样共享数据就不会复制到多个 IPU,而是在不同的流水线阶段保持可用——因为它们不是按顺序执行的。
我正在尝试使用 Graphcore 的 PopART framework(Poplar API 的一部分)来实现一个基本的流水线模型,以加速我的模型,该模型分布在多个处理器上。
我正在关注他们的 example code, but I notice the example does not use the pipelineStage()
call, which is used in some of their other applications (namely Bert),而是使用 virtualGraph()
来定义处理器,操作应该 运行。
下面示例的一小段:
# Dense 1
W0 = builder.addInitializedInputTensor(
init_weights(num_features, 512))
b0 = builder.addInitializedInputTensor(init_biases(512))
with builder.virtualGraph(0):
x1 = builder.aiOnnx.gemm([x0, W0, b0], debugPrefix="gemm_x1")
x2 = builder.aiOnnx.relu([x1], debugPrefix="relu_x2")
# Dense 2
W1 = builder.addInitializedInputTensor(init_weights(512, num_classes))
b1 = builder.addInitializedInputTensor(init_biases(num_classes))
with builder.virtualGraph(1):
x3 = builder.aiOnnx.gemm([x2, W1, b1], debugPrefix="gemm_x3")
x4 = builder.aiOnnx.relu([x3], debugPrefix="relu_x4")
相反,Bert 示例似乎创建了一个结合了 virtualGraph()
和 pipelineStage()
的上下文:
self.stack.enter_context(self.builder.pipelineStage(self.pipelineStage))
我不确定哪种风格应该是首选。仅使用 virtualGraph()
有什么影响吗?
virtualGraph
和 pipelineStage
是 Graphcore PopART 框架中的两个不同概念,尽管它们也相关。
virtualGraph
(参见您提到的Setting the IPU number for operations section of the PopART User Guide and the PopART C++ API for reference) enables splitting a graph into multiple parts, to run on multiple IPUs. Using virtualGraph
on its own, as shown in the code example,表示运行在模型的分配部分上顺序排列。
另一方面,pipelineStage
允许您将图形分成几个阶段,如果可能的话,可以 运行 在不同的 IPU 上并行。您可以灵活地选择应该在每个流水线阶段放置哪些操作。流水线由选项 opts.enablePipelining
启用(参见 PopART C++ API)。
默认情况下,流水线隐式地为每个 virtualGraph
创建一个 pipelineStage
。
但是,可以为单个 virtualGraph
指定多个 pipelineStage
。当模型的两个部分共享相同的大型数据集时,这可能很有用。这就是 BERT 模型中使用 pipelineStage
和 virtualGraph
组合的原因:BERT 有一个很大的嵌入矩阵,用于模型的开始和结束。可以将两个操作放在同一个 virtualGraph
中以位于同一个 IPU 上,这样共享数据就不会复制到多个 IPU,而是在不同的流水线阶段保持可用——因为它们不是按顺序执行的。