DSL 递归
在DSL中编写一个递归函数
旧版本
本页面是关于 Kubeflow Pipelines V1,有关最新信息,请参见 V2 documentation。
注意,虽然V2后端能够运行由V1 SDK提交的管道,我们强烈建议迁移到V2 SDK。作为参考,V1 SDK的最终版本是kfp==1.8.22,其参考文档在这里可用。
本页面描述了如何在Kubeflow Pipelines SDK提供的领域特定语言(DSL)中编写递归函数。
动机
递归是一种几乎所有语言都支持的特性,用于以简洁的方式表达复杂的语义。在机器学习工作流中,递归特别重要,以使多个训练轮次、迭代模型分析和超参数调优等功能得以实现。递归支持还涵盖循环特性,因为它允许根据动态条件执行和退出相同的代码块。
如何编写递归函数
装饰器
如下面所示,用 kfp.dsl.graph_component 装饰递归函数。该装饰器不需要任何参数。
import kfp.dsl as dsl
@dsl.graph_component
def graph_component_a(input_x):
with dsl.Condition(input_x == 'value_x'):
op_a = task_factory_a(input_x)
op_b = task_factory_b().after(op_a)
graph_component_a(op_b.output)
@dsl.pipeline(
name='pipeline',
description='shows how to use the recursion.'
)
def pipeline():
op_a = task_factory_a()
op_b = task_factory_b()
graph_op_a = graph_component_a(op_a.output)
graph_op_a.after(op_b)
task_factory_c(op_a.output).after(graph_op_a)
函数签名
将函数签名定义为标准的Python函数。输入参数是 PipelineParams。
函数主体
类似于管道函数主体,您可以实例化组件,创建 条件,使用函数签名中的输入参数,并在组件之间显式指定依赖关系。在上面的示例中,递归函数内部创建了一个条件,并在条件内部创建了两个组件 op_a 和 op_b。
在管道函数中调用递归函数
您可以将管道/组件的输出传递给递归函数,并使用 after() 函数显式指定依赖关系,这类似于 ContainerOp。在上面的示例中,管道中定义的 op_a 的输出被传递给递归函数,并且 task_factory_c 组件被指定依赖于 graph_op_a。递归函数也可以被显式指定依赖于 ContainerOps。例如,graph_op_a 在管道中依赖于 op_b。
更多示例
这里是另一个例子,其中递归函数调用位于函数体的末尾,类似于 do-while 循环。
import kfp.dsl as dsl
@dsl.graph_component
def graph_component_a(input_x):
op_a = task_factory_a(input_x)
op_b = task_factory_b().after(op_a)
with dsl.Condition(op_b.output == 'value_x'):
graph_component_a(op_b.output)
@dsl.pipeline(
name='pipeline',
description='shows how to use the recursion.'
)
def pipeline():
op_a = task_factory_a()
op_b = task_factory_b()
graph_op_a = graph_component_a(op_a.output)
graph_op_a.after(op_b)
task_factory_c(op_a.output).after(graph_op_a)
限制
- 类型检查不适用于递归函数。换句话说,注释在递归函数签名上的类型信息将不会被检查。
- 由于递归函数的输出无法动态解析,下游的 ContainerOps 无法访问递归函数的输出。
- 一个已知的 问题 是,当函数体内有多个递归函数调用时,递归无法正常工作。
下一步
- 查看 递归示例
Last modified July 6, 2024: 更新 Kubeflow Pipelines 旧版 V1 警告文本 (#3792) (833cc62)