DSL 递归
旧版本
此页面是关于 Kubeflow Pipelines V1 的内容,请查看 V2 文档以获取最新信息。
注意,虽然 V2 后端能够运行由 V1 SDK 提交的 pipeline,但我们强烈建议迁移到 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。
函数体
与 pipeline 函数体类似,你可以实例化组件,创建条件,使用函数签名中的输入参数,并明确指定组件之间的依赖关系。在上面的示例中,递归函数内部创建了一个条件,并且在条件内部创建了两个组件 op_a 和 op_b。
在 pipeline 函数中调用递归函数
你可以将 pipeline/组件的输出传递给递归函数,并使用 after() 函数明确指定依赖关系,类似于 ContainerOp。在上面的示例中,pipeline 中定义的 op_a 的输出被传递给递归函数,并且 task_factory_c 组件被指定依赖于 graph_op_a。递归函数也可以明确指定依赖于 ContainerOps。例如,graph_op_a 在 pipeline 中依赖于 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 无法访问递归函数的输出。
- 一个已知的 问题 是,当函数体中有多个递归函数调用时,递归不起作用。
下一步
- 请参阅递归示例