开一家连锁餐厅,后厨最怕什么?主厨凭经验指挥,今天说“盐少许”,明天新来的帮厨不知道“少许”是多少;想追查昨晚哪锅汤出了问题,没人记得当时怎么做;开了第二家分店,还得把主厨整个人“复制”过去。直到有一天,店长决定把每道菜的做法写成标准菜谱,明确食材、步骤、火候与时间,统一存入文档仓库,每次修改都有版本记录、有署名、有日期。分店做菜不再靠运气,味道稳定,新人也能照单施工。这套“菜谱化”思路,就是流水线即代码(Pipeline as Code)的生活原型。 软件开发里的“流水线”,是代码从提交到上线自动经历的一连串工序:编译、测试、打包、部署。传统 CI/CD 工具像“凭经验的主厨”,在网页上点点鼠标配置,配置沉睡在服务器里,没人审阅、难以复用、改一次怕一次。Pipeline as Code 则把这份“菜谱”写成代码,和业务代码一起放进 Git 仓库,接受 Code Review、打版本号、被复用、被审计。从此,交付不再依赖某个“老师傅”,而是依赖一套有章法的工程体系。
对 AI 从业者而言,掌握流水线即代码,意味着把模型的训练、评估、上线从“手动炼丹”变成“自动流水线”。这是 AI 工程化的硬技能,也是面试与晋升中的差异化优势。
目录
可视化演示
展示Pipeline as Code的版本化流水线结构、条件触发规则评估与DAG并行执行优化,交互切换模式查看CI/CD流水线核心机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
流水线结构:版本化CI/CD YAML配置定义stages和jobs流水线版本可控可审计 每个stage包含多个job按序执行job间可传递产物 pipeline=stages[jobs[steps]] 配置文件与应用代码同仓库管理实现基础设施即代码
核心概念:从主厨笔记到标准菜谱
连锁餐饮最头疼的事情,不是菜做得不好吃,而是每一家分店的味道都不一样。老店的李师傅凭多年经验做菜,每次都能拿捏“盐少许”,可新店的帮厨不知道“少许”是多少;李师傅一休假,招牌菜就没了魂。直到总部决定:把所有菜品的做法写成标准菜谱,明确食材、步骤、火候与时间,统一存入文件柜,每份菜谱都盖着版本章。分店做菜不再靠运气,而是按菜谱执行,味道稳定,新店也能快速复制手艺。
软件开发里的“流水线”就是这样的后厨工序:代码从提交到上线,要自动经历编译、测试、打包、部署,这一整套自动化工序链称为 CI/CD 流水线。早期 CI/CD 工具像“凭经验的主厨”,在网页上手工点选配置,配置沉睡在服务器里,别人看不懂、改不动、也不敢动。流水线即代码(Pipeline as Code)则把这一整套工序写成代码文件——例如 Jenkinsfile、.gitlab-ci.yml、GitHub workflow 的 YAML——放进 Git 仓库统一管理。
一旦流水线变成代码,它的身份就彻底变了:它有了提交记录,有了版本号,可以接受同事的 Code Review,可以被复制到新项目复用,也可以回滚到任意历史版本。主流 CI/CD 平台都提供了声明式的流水线语法,工程师用一个文件就能描述完整的交付工序,这就是 Pipeline as Code 的核心姿态。
回到后厨的比喻:标准菜谱改变了连锁餐厅的扩张方式;Pipeline as Code 则改变了团队的交付方式。今天一个 AI 团队的模型训练、评估、上线流程,也能用这样一份“数字化菜谱”串起来,让交付从“老师傅的手艺”变成“标准化的工业能力”。
- SourceCode(源码输入)流水线的原料,一次代码提交(commit)就是一份待加工的食材。
- Build 构建工序把源码编译打包成可运行的产物,相当于把食材加工成半成品。
- Test 测试工序对构建产物做质量校验,相当于出锅前的试菜,过不了就退回重做。
- Deploy 部署工序把验证通过的产物发布到目标环境,相当于把菜端上客人的餐桌。
设计理念:菜谱的三个“可”
标准菜谱库为什么能成功?因为它同时满足三个要求:第一,可追溯——菜谱的每一处改动都有记录,盐从 5 克改成 3 克,是谁、在什么时候、为什么改,翻档案就知道;第二,可复用——新分店不用重新发明菜谱,直接领取母版再微调;第三,可审计——菜品出了问题,顺着菜谱版本能定位到当时的操作规范,责任边界清清楚楚。
Pipeline as Code 的设计理念正是这三个“可”。可追溯:流水线文件的每次修改都伴随一条 commit 记录,构建产物上标注着对应的源码 commit 号,任何一次发布都能反查到“它从哪段代码来”;可复用:一份流水线模板可以在多个项目间共享,团队把公共检查步骤沉淀成共享库,新项目几分钟接入;可审计:流水线执行日志持久化,谁触发的、哪个阶段耗时长、哪次变更引入了缺陷,事后全部可查。
另一个重要理念是“声明式优先”。声明式语法只描述“最终要达成什么状态”,比如“我要一个构建阶段、跑测试、推镜像”,而不手写每一步的执行指令。好比菜谱只写“小火慢炖 40 分钟”,而不是叮嘱厨师“把旋钮向右拧 60 度、等 40 分钟再拧回来”。声明式的优势是易读、安全、适合平台化管控,这也是 Jenkinsfile 和各类 YAML 流水线的共同倾向。
这三个“可”,就是流水线作为工程资产的底气。就像连锁餐厅可以放心扩张,因为菜谱体系让每一家分店都站在统一的质量基线之上——工程团队也可以放心地让流水线代劳重复劳动,因为一切有迹可循、有据可查。
- Commit(提交)开发者的每一次改动在仓库中留下唯一编号,这是追溯的起点。
- Artifact(产物)构建系统将提交转化为产物,产物携带来源 commit 的指纹信息。
- Release(发布)产物被部署到环境后形成版本发布,与产物一一对应。
- Log(日志)全过程的执行日志被持久化,让审计可以顺着链条回溯。
Jenkinsfile:中央厨房的固定骨架
有了标准菜谱还不够,菜谱本身还得有统一的版式,不然每家分店写出来的菜谱五花八门,总部根本没法审。Jenkins 用 Jenkinsfile 定义了流水线的统一骨架:先声明 pipeline 根块,再指定 agent 运行环境,然后用 stages 组织阶段,每个 stage 里用 steps 描述具体动作。
一个典型的声明式 Jenkinsfile 长这样:pipeline 块是整份菜谱的外皮;agent 指明“这张菜谱在哪个厨房执行”,例如 agent any 表示任意可用节点;stages 是菜谱正文,按顺序列出阶段——检出代码、构建镜像、跑测试、推送制品;stage 内部可以继续嵌套 stage 或 parallel 实现并行。post 块负责收尾,无论成功失败都能执行清理或通知。
把流水线的执行结构公式化,就是阶段的复合:
Jenkinsfile 写进仓库后,团队 review 的就不再是“你点了什么按钮”,而是“你的工序是否合理”。统一骨架确保不同项目的流水线有章法——就像连锁店里每一份菜谱都遵循同样的版式,总部审阅效率大大提升。
- pipeline 根块声明式流水线的顶层容器,相当于菜谱的封面。
- agent 环境指定流水线运行在哪台机器或哪个容器中。
- stages 阶段序列按顺序排列的 S₁ 到 Sₙ,串行执行,前序失败则后续不跑。
- post 收尾无论成败都会触发的清理与通知逻辑,相当于厨房打烊后的整理。
GitHub Actions:客人点单才开工
如果说 Jenkinsfile 是“后厨常备的烤箱程序”,那 GitHub Actions 更像是“接到订单才开火的点单式厨房”。它的核心理念是事件驱动:仓库里发生 push、pull_request、issue 创建等事件时,自动触发对应的工作流(workflow)。
工作流定义在 .github/workflows/ 目录下的 YAML 文件中。一个 workflow 由多个 job 组成,job 之间可以并行,也可以用 needs 表达依赖;每个 job 内部包含多个 step,step 执行具体命令或调用现成的 action(第三方封装的步骤)。例如一个典型工作流:push 到 main 分支触发 job "build-test",它内部先 checkout 代码,再 setup-node,再 npm test。整个过程全部用 YAML 声明,一次编写、处处复用。
事件触发可以写成条件表达式:
“点单式”的好处是精确与轻量:没有事件进来,流水线就不消耗资源;事件一进来,整个工作流自动运转。就像客人不落座,后厨不会提前把菜炒好——只有当代码真正变化时,流水线才启动,把每一分算力都花在刀刃上。
- on 触发事件声明哪些仓库活动会唤醒工作流,如 push、PR、定时。
- Workflow 工作流一个 YAML 文件中定义的完整流水线,包含若干 job。
- job 任务组工作流中的执行单元,可并行也可串行依赖。
- step 步骤job 内部的最小执行动作,如跑一条命令或调用一个 action。
GitLab CI:传菜机器人 Runner 在行动
连锁店的故事继续往下走:有了菜谱,还得有传菜的人。GitLab CI 的架构里,.gitlab-ci.yml 是菜谱,Runner 则是把菜从后厨送到餐桌的传菜机器人。Runner 可以安装在专用服务器或 Kubernetes 集群上,轮询 GitLab 服务器派发的任务,拿到 job 后创建容器执行。
.gitlab-ci.yml 里通过 stages 定义阶段先后顺序:默认有 build、test、deploy 三个阶段,所有 job 根据所属 stage 排队执行。同属一个 stage 的 job 可以并行跑;前一个 stage 全部成功,才会进入下一个 stage。job 可以声明 artifacts 把产物传递给后续阶段,也可以声明 cache 缓存依赖加速构建。
阶段间的依赖与产物传递可以表示成:
有了 Runner 这个“传菜机器人”,流水线的执行与仓库配置解耦:配置文件只管定菜谱,Runner 负责跑腿。哪台机器空闲、哪个容器环境就绪,都由 Runner 统一调度——后厨规模再大,传菜也不会乱。
- Stage 阶段组把 job 归类为先后逻辑批次,如 build → test → deploy。
- Runner 执行器实际拉取作业并运行的代理,让 job 在隔离容器中执行。
- Artifact 产物阶段产出的文件,被传给后续阶段使用。
- Stage_{i+1} 下一阶段消费产物并继续推进,直到流水线结束。
条件触发:不是每道菜都要走满全流程
客人点一份凉菜,后厨不需要开烤箱;点一份汤,也不一定需要动用三四个灶头。聪明的厨房会按订单内容决定开哪些设备。流水线也一样:不是每次提交都要跑完整的构建、测试、部署三件套,用条件触发可以精准控制“什么时候跑、跑哪些内容”。
GitLab CI 用 rules 精确控制 job 的运行条件;GitHub Actions 在 on 里支持 paths 和 branches 过滤;Jenkins 则提供 when 指令配合分支与变更集判断。常见场景包括:只在 main 分支上执行部署任务;只在 src/ 或模型代码目录有修改时才重跑训练;高风险操作需要人工点“批准”才继续;用参数化构建让同一份流水线按输入参数适配不同环境。
条件触发可以抽象成一个判定函数:
条件触发让流水线学会“看单下菜”:轻量改动走轻量流程,关键改动走完整流程,人工审批卡住高风险环节。这样既守住了质量红线,也不至于让每一次小改动都背负整套流程的重担。
- branch = main 分支条件只有主干分支的提交才满足部署条件。
- path ∈ src/ 路径条件改动文件必须位于 src 目录及其子目录,避免文档改动也触发构建。
- ∧ 逻辑与两个条件同时满足,才输出 1(执行)。
- run = 1 / 0 判定输出1 表示触发,0 表示跳过,其余情况不浪费资源。
并行优化:三个灶头同时开火
宴席上八道菜,如果只用一个灶头,客人要等两小时。有经验的厨师会同时开三个灶:炖汤的灶、炒菜的灶、蒸鱼的灶各司其职。流水线的并行优化就是同样的道理——把不存在依赖关系的阶段拆开,让它们同时执行,总耗时取决于最慢的那条线,而不是所有阶段之和。
Jenkins 的 parallel 指令可以在一个 stage 内并行多个分支;GitLab CI 同 stage 的多个 job 天然并行;GitHub Actions 不同的 job 默认并行执行,还可以组合出矩阵构建(matrix),一次生成多平台、多版本的组合矩阵。比如同时跑 Python 3.9/3.10/3.11 的测试,或者同时构建 CPU 版和 GPU 版的镜像。
串行与并行的耗时差异可以用模型刻画:
并行不是免费的午餐:每个并行任务都要占用 CPU、内存和构建机资源,任务过载反而会让整体变慢。聪明的团队会先画“工序依赖图”,把能并行的挑出来,再给并行池设置资源上限。就像主厨同时开三个灶是本事,硬开八个灶导致火力不足,菜反而炒不熟。
- 串行阶段求和必须按顺序执行的阶段,耗时逐一相加。
- 并行组取最大值同一时刻并行执行的阶段,总耗时由最慢者决定。
- T_总 总耗时两部分之和,就是整条流水线的端到端时间。
模板复用:一份母版菜谱遍及千家分店
连锁餐厅开得越多,越能体会到“母版”的价值:总部把基础菜谱抽象成模板,分店只需替换本地食材和口味参数,就能快速生成自己的菜品。流水线的模板复用也是同一套路——把公共的构建、检查、发布逻辑沉淀成模板,各项目通过参数注入差异部分。
Jenkins 的共享库(Shared Library)把公共函数和步骤封装成可版本化的代码库,Jenkinsfile 直接引用;GitLab CI 用 include 关键字引入其他仓库或本仓库中的模板文件,配合 variables 传递参数;GitHub Actions 则支持可复用工作流(reusable workflows),一个 workflow 可以像调用函数一样被另一个 workflow 调用。模板与实例的关系可以写成:
模板复用要警惕“为了抽象而抽象”:参数越多、嵌套越深,模板越难读懂。好的做法是从两三个真实使用的流水线中提炼公共部分,保持参数精简,并给模板本身打上版本号——就像连锁店的母版菜谱也要定期修订、发行新版,而不是一次写死、永不再动。
---
- Template 模板抽取了公共工序的流水线骨架,如安全检查、镜像构建。
- 参数 x₁…xₖ项目名、分支名、镜像标签等差异化输入。
- Pipeline_具体 实例模板实例化后得到的、可用于具体项目执行的流水线。
AI 应用场景
场景 1:AI 模型训练流水线标准化
阶段:模型交付与部署
解决的问题:算法工程师训练模型时常常手动执行数据清洗、训练、评估,参数散落在笔记本里,实验结果不可复现。传统手工流程还容易漏掉模型版本记录,上线后出了问题,说不清是哪个 commit 训出来的。
具体案例:某 AI 团队在 GitLab 中创建一条训练流水线:push 训练代码触发 Runner 在 GPU 机器上依次执行数据清洗 → 模型训练 → 指标评估,评估通过的模型产物自动打包并记录 commit 号,实验报告自动归档。任何一次实验都能复现,模型与代码版本严格绑定。
场景 2:多服务流水线治理与合规审计
阶段:工程系统治理
解决的问题:多个 AI 服务各自维护流水线,脚本复制粘贴、安全规范执行程度参差不齐,审计时无法快速交代“哪个环节、谁、改了什么”。
具体案例:平台团队用 Jenkins 共享库沉淀安全检查、镜像扫描、许可证检查等公共步骤,各服务 Jenkinsfile 通过共享库统一引用。安全团队只需升级共享库的一个版本,所有服务的流水线同时生效,审计日志集中可查。
场景 3:模型灰度发布的条件触发
阶段:发布运维
解决的问题:模型服务上线比普通应用风险更高,不能每改一次参数就全量发布,需要精准控制发布范围与时机。
具体案例:用 GitHub Actions 定义发布工作流:仅当标签以 v 开头且路径含 model/ 时触发灰度 job,灰度比例由参数控制,人工批准后再扩展生产流量。条件触发保障了高风险变更的审批可控、发布可控。
Python 演示
import timefrom concurrent.futures import ThreadPoolExecutor def fake_task(name, seconds): print("[%s] 开始, 预计 %.2fs" % (name, seconds)) time.sleep(seconds) return name, seconds class Pipeline: def __init__(self, name): self.name = name self.stages = [] def add_stage(self, name, seconds): self.stages.append((name, seconds)) def run(self, parallel=False): print("== %s 流水线 (并行=%s) ==" % (self.name, parallel)) t0 = time.time() if parallel: with ThreadPoolExecutor(max_workers=len(self.stages)) as pool: results = list(pool.map(lambda s: fake_task(*s), self.stages)) else: results = [fake_task(*s) for s in self.stages] total = time.time() - t0 slowest = max(seconds for _, seconds in results) print("总耗时: %.2fs, 最慢阶段: %.2fs" % (total, slowest)) return total if __name__ == "__main__": pipeline = Pipeline("AI 服务发布") pipeline.add_stage("检出", 1.0) pipeline.add_stage("构建", 2.5) pipeline.add_stage("测试", 1.8) pipeline.add_stage("部署", 1.2) serial_time = pipeline.run(parallel=False) parallel_time = pipeline.run(parallel=True) print("并行比串行节省: %.2fs" % (serial_time - parallel_time))
self.stages = []用列表保存阶段序列,相当于 Jenkinsfile 中的 stages 集合。def add_stage(self, name, seconds)注册一个新阶段,相当于在流水线配置里追加一个 stage。with ThreadPoolExecutor(max_workers=len(self.stages)) as pool:创建与阶段数等宽的线程池,让各阶段可并发执行。results = list(pool.map(lambda s: fake_task(*s), self.stages))把每个阶段分派给线程池并行调用,模拟 parallel 指令。results = [fake_task(*s) for s in self.stages]串行模式下逐个执行阶段,对应串行管道。slowest = max(seconds for _, seconds in results)并行总耗时由最慢阶段决定,对应公式中的 max 项。serial_time - parallel_time输出并行带来的时间节省,让优化效果一目了然。
常见误区
误区我以为只要把平时手敲的命令改成 YAML 格式放进仓库,就算完成了流水线即代码改造,根本不关心阶段怎么划分、失败怎么处理。
正解流水线即代码的要点是结构化,而不是换个地方写脚本。真正的代码化流水线要有清晰的阶段划分、产物传递和错误处理机制。YAML 只是载体,工序设计才是灵魂。好比菜谱不是把“随便炒炒”四个字写进文档就完事,而是要写清食材、火候、步骤和验收标准。
为何常见:初学看到的示例大多只有几行 script,很容易误以为流水线即代码等于“在配置里写命令”。
误区一遇到动态分支、循环等复杂需求,我就想放弃声明式语法,退回 Groovy 或 shell 全脚本实现,认为这样才灵活。
正解声明式是主流推荐,因为它易读、安全、适合平台管控。遇到复杂逻辑时,正确做法是把逻辑封装进共享库函数或脚本块,再由声明式调用,而不是抛弃声明式骨架。二者可以共存:声明式做骨架,脚本块做局部细节。
为何常见:复杂需求确实存在,而声明式语法对新手的约束感很强,容易让人误以为它是“玩具”。
误区我写完 Jenkinsfile 或 YAML 就直接 push,认为配置不是代码,不需要测试,结果经常因为缩进、引号或字段拼写错误导致流水线一跑就挂。
正解流水线配置也是代码,同样需要质量保障:用 yamllint 做语法检查、在测试分支先试跑、用 CI 前置任务做配置自检。很多流水线事故的根因就是 YAML 写错,而不是业务代码有问题。
为何常见:它叫“配置”或“文件”,让人潜意识里放松了警惕,但它实际承担着生产行为编排的职责。
误区看到并行优化的演示很心动,我把所有能拆的阶段全部拆开并发执行,结果构建机资源被打满,任务互相排队,整体反而更慢。
正解并行受制于依赖关系和资源上限。先画阶段依赖图,只并行真正无依赖的任务,并给并行池设置 CPU、内存配额;并行度不是越高越好,而是在资源与耗时之间取平衡。就像主厨同时开三个灶是本事,硬开八个灶导致火力不足,菜反而炒不熟。
为何常见:演示场景往往忽略资源约束,让人产生“并行无成本”的错觉。
误区为了追求“一次编写处处运行”,我把模板参数加到几十个,嵌套多层 include,结果每个项目接入时都要读半天文档,模板本身成了新的维护负担。
正解好的模板是从两三个真实流水线中提炼出来的,参数精简、职责单一,并跟随真实需求迭代版本。过度抽象会制造“配置地狱”,违背了流水线代码化“易读、可维护”的初衷。
为何常见:工程师普遍有复用洁癖,容易把“抽象”本身当成目标,忽略了抽象的成本。
面试题
Q1. Pipeline as Code 的核心做法是什么?
- A.在网页图形界面上手工配置流程
- B.将流水线写成代码并纳入版本库管理← 正确答案
- C.使用 shell 脚本在服务器定时执行
- D.登录服务器逐条执行部署命令
答案:B。Pipeline as Code 的本质是把流水线定义写成代码文件并纳入版本管理,获得可追溯、可复用、可审计的能力。A 是传统网页配置,C、D 是手工脚本操作,都不具备版本化与评审能力。
Q2. 下列哪个文件是 GitLab CI 的流水线配置文件?
- A.Jenkinsfile 主流水线配置文件
- B.GitHub workflow 配置文件
- C..gitlab-ci.yml 配置文件← 正确答案
- D.docker-compose 服务编排文件
答案:C。GitLab CI 使用 .gitlab-ci.yml 定义流水线;Jenkinsfile 属于 Jenkins,workflow 配置属于 GitHub Actions,docker-compose 用于容器编排而非 CI 流水线。
Q3. Jenkinsfile 中声明式流水线的根块关键字是什么?
- A.stages 声明所有阶段的集合
- B.step 代表最小的执行动作
- C.pipeline 声明流水线的根块← 正确答案
- D.agent 指定流水线的运行环境
答案:C。Jenkinsfile 的根块是 pipeline,stages、step、agent 都是 pipeline 内部的组成部分。就像菜谱的封面是“菜谱”本身,而不是里面的食材清单。
Q4. 在 GitHub Actions 中,一个 job 内部的最小执行单元是什么?
- A.stage 流水线的阶段划分
- B.step 任务内的最小执行单元← 正确答案
- C.runner 工作流的运行机器
- D.artifact 流水线产物管理
答案:B。GitHub Actions 中 job 由若干 step 组成,step 是最小执行单元,可以是一条命令或一个 action。stage 是 Jenkins 的概念,runner 是执行环境,artifact 是产物。
Q5. 关于声明式流水线与脚本式流水线,下列说法正确的是?
- A.声明式允许编写任意全局命令
- B.脚本式比声明式更容易阅读
- C.声明式重描述脚本式重步骤← 正确答案
- D.两者在语法上完全等价
答案:C。声明式强调“描述要什么”,脚本式强调“一步步怎么做”。声明式更易读、更安全,脚本式更灵活但更难维护。二者不是等价关系,也不能随意写全局命令。
Q6. 某团队希望仅当 main 分支且 src/ 目录有变更时才触发流水线,应优先采用哪种方式?
- A.分支限制加上路径过滤规则← 正确答案
- B.每次提交都全量运行流水线
- C.在脚本里人工判断并跳过
- D.关闭自动触发改用定时任务
答案:A。GitHub Actions 的 on.push.branches 与 paths、GitLab 的 rules 都能组合实现“分支 + 路径”过滤。B 会浪费资源,C 是在执行期补救而非触发期控制,D 改变了触发语义。
Q7. 流水线并行优化中,总耗时的正确计算方式是?
- A.串行阶段和加并行组取最长← 正确答案
- B.所有阶段耗时直接相加求和
- C.并行组和加串行组取最短
- D.只看最慢阶段其他忽略
答案:A。并行阶段的耗时取组内最大值,串行阶段逐一累加,二者之和才是总耗时。B 忽略并行优化,C 的“取最短”不符合实际,D 忽略串行阶段。
Q8. 下列属于流水线模板复用机制的有哪些?
- A.Jenkins 共享库机制复用← 正确答案
- B.GitLab CI 的 include 模板← 正确答案
- C.GitHub 可复用工作流← 正确答案
- D.Docker 镜像分层缓存机制
答案:ABC。Jenkins Shared Library、GitLab CI include、GitHub Actions reusable workflows 都是流水线模板复用手段。Docker 镜像分层缓存属于镜像构建优化,不解决流水线配置复用问题。
Q9. 关于流水线的可追溯性,下列说法正确的是?
- A.流水线每次修改都有提交记录← 正确答案
- B.产物可反查到对应的源码提交← 正确答案
- C.只有网页点按才能保证可追溯
- D.审计日志明确改动人与时间← 正确答案
答案:ABD。代码化流水线的改动天然有 commit 记录;构建产物携带源码 commit 指纹;审计日志能定位改动人与时间。C 恰恰是传统网页配置的缺陷,手工点按难以追溯。
Q10. 某数据流水线含“数据清洗、模型训练、模型评估、发布”四个阶段,资源允许时训练与评估可并行。下列说法正确的有?
- A.训练与评估并行可降低总耗时← 正确答案
- B.并行可能因资源上限互相等待← 正确答案
- C.数据清洗必须排在训练之前← 正确答案
- D.发布可与训练同时进行不受限
答案:ABC。并行后总耗时取训练与评估的较长者,故 A 对;并行会争抢 GPU 等资源,B 对;数据清洗是训练的前置依赖,C 对;发布必须等评估完成,不能与训练同时进行,D 错。这正体现了并行优化中“先画依赖图、再设资源上限”的原则。