老城区有一家面馆,招牌是祖传的红烧牛肉面。老师傅收了个徒弟,把配方倾囊相授:牛肉两斤、酱油三勺、慢火炖两小时。徒弟回自己店里照着做,味道却总差一口气。老师傅到徒弟的厨房一看,笑了——徒弟用的酱油是另一个牌子,锅是电磁炉不是煤气灶,连“三勺”用的勺子都不一样。原来,真正的好配方,必须把食材、火候、器皿全部写明白,才能让任何人在任何厨房做出同一碗面。 机器学习的实验,也是一碗“红烧牛肉面”。论文里写的“学习率 0.001、训练 100 轮”,就像是“酱油三勺”。换个环境、换台机器、换个随机种子,训练结果就可能完全不同。实验复现(Experiment Reproducibility),就是给 AI 研究写一本精确到克、精确到秒的“菜谱”,让任何一个研究团队,照着同一本菜谱,在任何厨房里,都能端出同一碗面。
对 AI 从业者而言,掌握实验复现是科研诚信的底线,也是团队协作的通行证。实验结果无法复现,再漂亮的指标也等于零。
可视化演示
实验复现,确保同一实验在不同环境下产生一致结果的机器学习研究基石。学习如何锁定代码版本依赖环境与随机种子,复现... 切换模式查看不同维度。
本节配有交互动效(进入视口后自动加载,约 3KB)。
实验复现性的:核心原理 动态展示关键流程与数据流动 交互控件调节参数实时渲染 f(x) = transform(x) 核心概念的可视化呈现
复现定义与价值
老师傅在面馆门口贴了一张告示:“本店牛肉面秘方,欢迎同行前来学艺。”真来了个同行,老师傅却只给人家看了三行字:牛肉两斤、酱油三勺、慢火两小时。同行回家照着做,端出来的面完全不是那个味。老师傅这才明白——他以为的“秘方”,其实只是一张模糊的购物清单。真正能称之为“配方”的东西,必须精确到每一种调料、每一分钟火候、每一度水温,让任何人在任何厨房都能复刻出同样的味道。机器学习里的“实验复现”,追求的就是这种“任何厨房都能做出同一碗面”的确定性。
在 AI 研究中,“复现”指在相同代码、相同数据、相同环境的条件下,重复实验能够得到与原始结果一致(或近似一致)的输出。它是科学实验的黄金标准:一个结果如果只有原作者在一台特定机器上才能跑出来,那就不是“实验结果”,只是“一次偶然”。复现能力决定了研究成果能否经得起同行检验,也决定了工程团队能否在他人工作的基础上继续推进。
要实现复现,需要抓住三个核心要素:代码(Code)、数据(Data)与环境(Environment)。代码是“菜谱文本”,数据是“食材”,环境是“厨房设备”,三者任何一个发生变化,结果都可能随之改变。这里还要区分两个容易混淆的概念:复现性关注“相同条件下结果一致”,而稳健性(Robustness)关注“条件轻微变化时结果仍然相近”。打个比方:复现性要求“同一份菜谱、同一间厨房,端出一模一样的面”;稳健性则要求“换一种品牌的酱油,面的味道依然不错”。两者侧重点完全不同。
理解了这三要素,再看老师傅的配方就豁然开朗了:他只写了“酱油三勺”,没写品牌(环境)、没写牛肉的产地(数据)、更没写勺子的大小(代码)。实验复现的起点,就是把这本模糊的购物清单,升级成一本精确到每一克的菜谱。
- $f$ 实验整体流程$f$ 代表从数据加载、模型训练到指标评估的完整管线,相当于从备菜到出锅的全部步骤。
- $C, D, E$ 三个输入$C$ 是代码版本(菜谱文字),$D$ 是数据集及其划分方式(食材与切法),$E$ 是依赖与硬件环境(厨房设备)。
- $\Longleftrightarrow$ 当且仅当“结果一致”与“条件相同”互为充要条件,只要一侧不成立,另一侧也必然不成立。
- $\approx$ 近似一致浮点运算存在极微小误差,复现不等价于逐位相等,而是误差在可接受范围内。
代码版本锁定
菜谱改了七版,第八版做出了惊艳的味道,可客人问“这版是怎么做的”,老师傅翻遍抽屉只找到七份手稿,第八版改良时在原文上涂改得面目全非。菜品好却无法复刻,这是厨师最大的遗憾。AI 实验也一样——训练出一个效果极佳的模型,但代码改来改去没留版本,两周后想回到那个状态,已经找不回当时的代码了。代码版本锁定,就是给每次实验的“菜谱”盖上一个永久的印章。
代码版本锁定的核心工具是版本控制系统,业界标准是 Git。Git 的每一次提交(commit)都会生成一个唯一的哈希值(通常是 SHA-1),这个哈希值由提交内容、父提交哈希和提交信息共同计算得出。也就是说,只要改动一个字符,哈希值就会完全改变。这个哈希值,就是菜谱某一版的“防伪编号”,任何人报出这个编号,就能精确找到那一版代码。
实操层面有三个要点。第一,实验开始前用 git commit 记录当前代码状态,并记下 commit 哈希;第二,对模型相关的核心代码(数据处理、网络结构、训练逻辑)要“冻结”,不允许实验中途改动;第三,用 git tag 或分支管理标记重要版本,相当于给菜谱标上“第 8 版·镇店之宝”,方便随时回退与复用。这些做法让“当时跑通的代码”不再是记忆中的模糊印象,而是可随时取出的精确快照。
有了版本锁定的习惯,任何一次实验都能回答“用的是第几版菜谱”。哪怕半年后要复刻当初的效果,只需要 git checkout 切回那个 commit,一切照旧——就像从档案柜里抽出那本盖了章的菜谱。
- $\text{tree}$ 文件快照提交那一刻仓库里所有文件的完整内容,相当于菜谱某一版的全文影印件。
- $\text{parent}$ 父提交哈希指向前一个版本的指针,把每次提交串成一条不可篡改的链条,改动任何一环都会断链报警。
- $\text{message}$ 提交信息开发者写的说明文字,解释这次改动的原因,方便未来回溯时快速定位。
- $\text{SHA-1}$ 哈希运算把上述内容压缩成固定长度的十六进制指纹,内容不同则指纹必不同。
依赖环境管理
老师傅终于写清了“酱油三勺”,徒弟却依然做不出那个味。老师傅到徒弟厨房一看,发现徒弟用的是电磁炉,自己用的是煤气灶,同样写着“慢火两小时”,实际火力差了十万八千里。菜谱写得再精确,厨房设备对不上也是白搭。机器学习实验里,Python 版本、NumPy 版本、PyTorch 版本、CUDA 版本——这些就是“煤气灶与电磁炉”的区别。
依赖环境管理,就是把实验运行所需的全部软件环境固定下来。最常用的工具是 requirements.txt 或 environment.yml:将每个依赖库的名称和精确版本号记录在案,如同菜谱附上一张“厨房设备清单:煤气灶(型号 XX)、铁锅(口径 32cm)、生抽(海天,500ml 装)”。另一层防线是虚拟环境(如 conda 或 venv),把不同实验的依赖隔离开来,避免“做牛肉面时不小心把烤蛋糕的烤箱参数带进来”。
更进一步,工程团队常用 Docker 把整个环境打包成镜像——包括操作系统、Python 解释器、所有依赖库,甚至环境变量。镜像用 Dockerfile 描述构建过程,用镜像 ID 唯一标识,运行时只需拉取同一个镜像,无论在哪台机器上,起到的都是同一间“标准厨房”。此外,GPU 型号、驱动版本等硬件差异也会影响数值结果,必须在记录中显式写明。
环境管理做得好,换任何机器都像走进了“连锁中央厨房”——设备、调料、灶台全部标准化。这也是今天 MLOps 领域把“环境即代码”当作基本信条的原因:环境不再是一次性的手工布置,而是一份可复制、可版本化的资产。
- $\mathcal{E}$ 环境快照整个运行环境的数学表示,是“厨房设备清单”的形式化版本。
- $p_i$ 依赖项Python、NumPy、PyTorch 等一个个软件组件,对应清单上的每一件设备。
- $v_i$ 固定版本每个组件锁定到具体版本号,防止“酱油从海天换成李锦记”这种版本漂移。
- 全量枚举 $n$清单必须完整,漏掉一个依赖都可能导致复现崩溃,正如缺一把锅铲就无法翻面。
随机种子控制
面条的最终口感,居然还取决于用哪一口井的水。老师傅的面馆后院有一口老井,水质清甜;徒弟用的是自来水,氯味重。菜谱里没提水,徒弟自然想不到。机器学习里,“水”就是随机性——数据打乱的顺序、模型参数的初始值、Dropout 的掩码,全都来自随机数。如果这些“随机水”不受控,实验结果就永远在“同一碗面”和“另一碗面”之间漂移。
计算机里的随机数其实是“伪随机数”:由一个随机数生成器(RNG)根据一个初始整数(种子 seed)按固定算法产生。同一个种子,生成的随机数序列完全一致;种子不同,序列就不同。因此,只要在实验开头固定随机种子(如 seed = 42),数据打乱、参数初始化等所有随机环节都会“回放”同一套序列。
在实际代码中,需要同时固定 Python 内置 random、NumPy、PyTorch 等多个随机源的种子,因为不同库各自维护独立的 RNG。此外,数据集的划分(训练集/验证集/测试集)也应基于固定种子进行,保证每次实验看到的是同一份“食材切法”。有些团队还会做“种子搜索”:在固定其他条件的前提下扫描多个种子,挑选结果最稳定的一个——就像试遍村中几口井,找出做面最好喝的那一口。
把“水”写进菜谱之后,徒弟终于能做出和师傅完全一样的面了。种子控制的意义正在于此:它消除了实验里最后一只“看不见的手”,让随机性从“玄学”变成“科学”,从“看运气”变成“可回放”。这也是为什么所有深度学习框架入门教程都会强调那一行 torch.manual_seed(42)。
- $k$ 种子整数一个普通整数(常用 42、2024 等),是伪随机序列的“起始密码”。
- $\text{RNG}$ 伪随机生成器一个确定性的数学函数,输入种子,输出一串看似随机但可预测的数。
- $\{r_1, r_2, \ldots\}$ 随机序列数据洗牌、权重初始化等操作按顺序从这个序列中取值,序列固定则一切固定。
复现验证流程
面出锅了,徒弟自己尝了一口,觉得“差不多”。但“差不多”是厨师最危险的三个字。老师傅让徒弟把两碗面并排放在一起,先看色泽,再闻香气,最后各夹一筷子细细对比——不是凭感觉,而是有一套固定的品鉴流程。实验复现的验证阶段,恰恰就是“并排品鉴”:不只看最终数字,更要系统化地对比。
复现验证流程的目标是回答一个问题:复现实验的结果与原始实验是否在可接受的误差范围内一致?流程一般分四步:第一步,建立基准——记录原始实验在固定验证集上的关键指标(准确率、损失、F1 等);第二步,在锁定代码、环境、种子的条件下重新运行实验;第三步,逐项对比指标与关键中间结果(训练曲线、验证曲线);第四步,根据相对误差是否小于预设阈值(如 1% 或 5%)判定复现是否成功。
验证过程要注意,对比的不仅是最终指标,还有训练过程的曲线形态。如果两条损失曲线形状迥异,即便最终准确率碰巧相同,也说明中间环节存在未被控制的差异。为此,实验记录中应保存每次迭代的损失值、验证集上的评估结果,以及模型产物的哈希值(如对模型权重文件取 MD5),以此检验产物是否“原封不动”。
有了这套品鉴流程,“差不多”就变成了“差多少”。两碗面并排一放,用同一把尺子量:颜色差几号、香气差几档、口感差几分,一清二楚。复现验证让“好吃”不再依赖厨师的自我感觉,而是变成可量化、可对比、可判定的客观标准。
- $M_{\text{orig}}$ 原始指标第一次实验得到的指标值,是整个复现过程的“标准答案”。
- $M_{\text{rep}}$ 复现指标按同样条件重跑得到的指标,是“徒弟做出来的成绩”。
- $\left|\frac{\cdot}{\cdot}\right|$ 相对误差用差值占原始值的比例衡量偏离程度,避免绝对值在大数值上失真。
- $\varepsilon$ 容差阈值预设的允许误差上限,工程上常用 1% 或 5%,超过即判定失败。
常见问题排查
即使菜谱、食材、环境都固定了,徒弟端出的面还是偶尔不对味。老师傅不慌不忙,拿出一个排查清单:第一步看菜谱是不是拿错了(版本),第二步看食材新不新鲜(数据),第三步看灶台火力准不准(环境),第四步看是不是用了别人的水(随机)。按顺序一项项排除,总能定位问题出在哪一环。实验复现失败时的排查,遵循完全相同的逻辑。
当复现实验与原始实验的结果不一致时,常见的排查路径是:先确认代码版本是否与记录一致(git diff 检查有没有未提交的改动);再确认数据集的版本与划分方式是否一致(同一份数据、同一个划分种子);然后核对依赖环境是否与记录一致(环境清单逐行比对);最后检查随机种子是否被正确设置,是否有全局或局部的随机源遗漏。这四步覆盖了实验结果不一致的绝大多数原因。
还有一些更隐蔽的“非确定性陷阱”:其一,GPU 并行计算中,不同的归约顺序会带来浮点误差,即便种子相同也可能产生微小差异;其二,某些库在特定环境下启用非确定性算法(如 TensorFlow 的某些 cuDNN 算子),需要显式关闭;其三,多进程或分布式训练中,数据加载的线程顺序不稳定也会引入变化。遇到这些问题,可以尝试强制使用确定性算法(如 torch.use_deterministic_algorithms(True))或固定 CPU 线程数。
老师傅的排查清单,本质上是把“做面”这件复杂的事拆成了四个可控环节,每个环节单独验证。AI 工程师排查复现问题时,也要有这种“分而治之”的耐心——不要一上来就怀疑算法改错了,先从最基础的版本与环境查起,把不确定性的范围一步步缩小,最终找到那口“变味的井”。
- $\Delta$ 差异项每一项前面的 $\Delta$ 表示“该要素与原始实验的差异”,差异不为零才可能导致结果变化。
- $\oplus$ 叠加组合多个差异可能同时存在、互相干扰,不能只检查一项就下结论。
- 依次归零排查先确保 $\Delta C = 0$,再检查 $\Delta D$,依此类推,每次只保留一个变量,其余全部固定。
实验记录追踪
老师傅有个泛黄的本子,上面密密麻麻记着:某年某月某日,阴天,用了新到的山西陈醋,炖了 2 小时 15 分钟,客人说偏酸。十年后,面馆想复刻当初老客人念念不忘的味道,老师傅翻出本子,按记录一步步还原,竟真的找回了那个味道。一本好笔记,是时间旅行者最可靠的飞船。AI 实验同样需要这样一本“泛黄的记录本”——这就是实验追踪(Experiment Tracking)。
实验追踪系统性地记录每次实验的完整信息:代码 commit 哈希、依赖环境快照、超参数配置、数据版本、训练日志、评估指标、模型产物路径。这些信息被统称为“元数据”(Metadata),它们与实验结果一一绑定,构成一条完整可追溯的链条。没有这条链条,三个月前的“辉煌结果”就只是一串来历不明的数字,既无法解释,也无法复用。
工程上,可以使用实验追踪工具(如 MLflow、Weights & Biases、TensorBoard)自动完成记录:每次运行自动打上时间戳,把超参数、指标曲线、产物文件归入同一个实验条目。团队协作时,统一的记录规范让所有人共享同一套“记录语言”:实验 ID、基线版本、负责人一目了然。配合版本锁定与环境管理,实验追踪让“复现”从一次性的手工劳动,变成系统化的自动化能力。
老师傅的泛黄本子记了三十年,面馆的每一碗面都有迹可循。AI 团队如果也能养成“凡实验必记录”的习惯,那么每一次训练都不会白费——它们会成为组织的记忆资产,让后来者站在前人的积累上继续前进,而不是反复在同一个坑里跌倒。这就是实验追踪最终的价值:让时间成为复现的朋友,而非敌人。
- $t$ 时间戳实验发生的时刻,是记录本上的日期,用于对齐时间线。
- $h_{\text{commit}}$ 代码哈希与版本锁定联动,明确哪一版菜谱产出了这一锅面。
- $\theta$ 与 $M$ 参数和指标记录火候设置与口味得分,是回看实验、分析成败的关键数据。
- $A$ 产物模型权重、日志文件等产出物,是“成品的照片与留样”,供后续检验与复用。
AI 应用场景
研究团队复现论文基线
阶段:实验验证与追踪
解决的问题:论文中的算法跑不出论文报告的指标,是研究者最头疼的事。传统做法是“手动搭环境、凭感觉调参”,既慢又不靠谱。通过锁定代码版本、依赖环境、随机种子,并记录完整的实验元数据,团队可以快速定位是“复现姿势不对”还是“论文本身有问题”,避免在不可复现的基线上浪费时间。
具体案例:某高校 NLP 实验室复现一篇 ACL 论文的对比实验。团队先记录论文作者开源的 commit 哈希,用 requirements.txt 重建环境,固定 seed=42 运行三次,发现 F1 与论文报告的 84.2% 相差 0.4%,在容差范围内判定复现成功,并基于此基线继续开展自己的改进实验。
算法工程师的模型回归测试
阶段:模型上线前验证
解决的问题:模型迭代时,新版本在训练集上效果提升,但线上效果变差,往往是因为“复现与测试不规范”导致的隐性退化。建立自动化的复现验证流程,每次迭代都重放基准实验,确保新模型没有悄悄“偷走”旧模型的能力。
具体案例:某电商推荐团队每次更新排序模型,都会用固定种子和冻结数据集重跑上一版本的离线评估,对比 AUC 与线上转化率。一旦新版 AUC 低于旧版 0.5%,系统自动拦截上线,并联动实验日志定位是特征变化还是模型结构变化引起的退化。
跨团队模型交接
阶段:模型交接与协作
解决的问题:A 团队训练好的模型交给 B 团队继续优化时,B 团队经常发现环境装不起来、数据找不到、指标对不上。基于实验记录追踪,交接的不仅是权重文件,而是包含环境镜像、代码哈希、元数据在内的完整“复现包”。
具体案例:某自动驾驶公司的感知模型从研究院移交到量产团队。研究院用 Docker 镜像加实验追踪平台导出一份复现清单(代码 commit、依赖版本、数据版本、训练 seed、评估脚本),量产团队在内部 GPU 集群上 48 小时内完整复现了 95% 的指标结果,顺利进入量产适配阶段。
Python 演示
import randomimport numpy as np def set_seed(seed): random.seed(seed) np.random.seed(seed) def generate_data(seed, n_samples=100): set_seed(seed) x = np.random.randn(n_samples, 1) y = 3.0 * x + 1.0 + 0.1 * np.random.randn(n_samples, 1) return x, y def train_model(x, y, epochs=30, lr=0.05, init_seed=0): set_seed(init_seed) w = np.random.randn(1, 1) b = 0.0 history = [] for epoch in range(epochs): pred = x @ w + b loss = np.mean((pred - y) ** 2) grad_w = 2 * np.mean((pred - y) * x) grad_b = 2 * np.mean(pred - y) w -= lr * grad_w b -= lr * grad_b history.append(round(float(loss), 8)) return float(w[0, 0]), b, history def main(): x, y = generate_data(seed=2024) w1, b1, hist1 = train_model(x, y, init_seed=7) w2, b2, hist2 = train_model(x, y, init_seed=7) w3, b3, hist3 = train_model(x, y, init_seed=99) same = np.allclose(hist1, hist2, rtol=1e-9) max_diff = max(abs(a - b) for a, b in zip(hist1, hist3)) print("实验1 vs 实验2(相同种子) 损失序列一致:", same) print("实验1 最终参数 w=%.6f b=%.6f" % (w1, b1)) print("实验2 最终参数 w=%.6f b=%.6f" % (w2, b2)) print("实验3(不同种子) 最终参数 w=%.6f b=%.6f" % (w3, b3)) print("实验1 与 实验3 损失最大差异:", round(max_diff, 8)) print("结论: 锁定种子则结果可复现, 更换种子则结果改变") if __name__ == "__main__": main()
random.seed(seed)锁定 Python 内置随机源,相当于固定“拌面时加盐的次序”。np.random.seed(seed)锁定 NumPy 的随机源,数据生成与权重初始化都从这里取数。x = np.random.randn(n_samples, 1)生成特征数据;种子固定,数据每次都完全相同。w = np.random.randn(1, 1)随机初始化权重,这是复现链路上最关键的环节之一。np.allclose(hist1, hist2, rtol=1e-9)对比两次训练的损失曲线,在极小容差内判定是否一致。max(abs(a - b) for a, b in zip(hist1, hist3))计算更换种子后损失序列的最大偏离,直观展示不复现的代价。
常见误区
误区“我在代码开头设置了 `np.random.seed(42)`,为什么换了台机器跑结果还是不一样?不是说固定种子就能复现吗?”
正解随机种子只是复现拼图的一块。结果还取决于代码版本、依赖库版本、数据划分方式、硬件环境等多个因素。固定种子保证的是“随机数序列”相同,但如果 NumPy 版本不同,同一个种子产生的序列可能不同;如果数据加载顺序不同,喂给模型的样本顺序也不同。完整复现需要三要素全部锁定,种子只是其中之一——就像菜谱写明了“用 3 号井的水”,但火候和食材不对,面依然不是那个味。
为何常见:很多教程把 seed = 42 当作“复现魔法一行流”,让初学者误以为种子是复现的全部;实际上它只是最容易被写进代码的一行,环境与版本等因素更隐蔽、更致命。
误区“做实验就是要一切尽在掌控,Dropout 这种随机操作最好关掉,否则没法复现。”
正解复现性不等于消除随机性。Dropout 等随机机制是模型设计的一部分,通过固定种子可以让它们的随机行为“可回放”,而不是必须关闭。复现追求的是“在相同条件下得到一致结果”,而不是“结果中没有任何随机成分”。好比菜谱里可以规定“加一撮盐”,只要明确“一撮”是多少克,加盐这个操作本身照样可以做。
为何常见:初学阶段容易把“随机”与“不可控”画等号,忽略了伪随机数生成器本身是确定性算法这一事实。
误区“项目代码我都同步到 Git 仓库了,留着以后肯定能复现,不用额外记录什么。”
正解代码只是三要素之一。没有依赖环境清单、数据版本记录和超参数日志,仅凭代码无法恢复实验现场。现实中的“代码能跑”往往暗含“恰好装好了正确的依赖”,换一台新机器就处处报错。完整复现需要同时保留代码版本、环境快照、数据与种子信息,四者缺一不可。
为何常见:代码是唯一“肉眼可见”的产物,环境、数据、种子等“隐形资产”容易被忽略;等到要复现时才发现,环境早已升级、数据早已清理。
误区“我用 GPU 训练,并行计算顺序每次都不一样,所以结果永远对不上,复现到这儿就算了。”
正解虽然 GPU 的非确定性归约(如并行累加的顺序)会引入极小的浮点差异,但通过固定随机种子、固定线程数、使用确定性算法(如 torch.use_deterministic_algorithms(True)),绝大多数情况下可以让结果稳定到可接受的误差范围内。工程上常见的做法是接受 1e-5 量级的误差,而不是要求逐位相等。把问题归咎于“GPU 玄学”,往往只是排查没做够的借口。
为何常见:非确定性算法导致的误差确实存在,且报错信息不直观,容易让人误以为“所有 GPU 训练都不可复现”,实际上可控性远比想象中高。
误区“复现实验跑完了,准确率 85.1%,和原来的 85.3% 差不多,肯定复现成功了吧?”
正解最终指标接近只是必要条件,不是充分条件。如果训练曲线的形状、验证集表现、中间产物的分布存在系统性差异,说明某个环节仍未锁定,只是“碰巧”在终点附近重合。严谨的复现验证应对比完整训练曲线、关键中间状态和多个评估维度,使用相对误差阈值判定,而不是只看一个孤立的数字。
为何常见:只看最终数字最省事,而曲线、中间状态对比需要额外记录和工具支持;“数字接近”的心理暗示也容易让人跳过深入核查。
面试题
Q1. 实验复现的核心目标是什么?
- A.让模型准确率越高越好
- B.相同条件下重复得到一致结果← 正确答案
- C.让实验结果超过论文基线
- D.减少实验所需的计算资源
答案:B。复现的目标是“同一菜谱同一厨房做出同一碗面”,B 准确描述了这一目标。A 是优化目标、C 是超越基线、D 是效率目标,都与复现的核心诉求无关。
Q2. 随机种子主要控制什么?
- A.代码编译时的顺序
- B.随机数生成器的序列← 正确答案
- C.模型推理时的速度
- D.训练数据集的规模
答案:B。随机种子的作用是决定伪随机数生成器的序列起点。固定种子后,数据打乱和参数初始化的顺序都可回放,就像锁定“用哪口井的水”。其余选项与随机种子无关。
Q3. 记录代码版本的推荐做法是?
- A.在文件名中加入日期后缀
- B.记录 Git 提交的唯一哈希← 正确答案
- C.把代码压缩包存到网盘
- D.在注释中写下修改人名字
答案:B。Git 提交哈希是代码版本的唯一指纹,任何内容变化都会生成新的哈希。文件名加日期、网盘存包、注释记人都无法精确锁定到“内容级”的版本。
Q4. Docker 在实验复现中的主要作用是?
- A.加速模型的训练速度
- B.封装代码与环境为镜像← 正确答案
- C.自动生成实验报告文档
- D.压缩模型的参数规模
答案:B。Docker 把操作系统库、Python 环境和依赖全部封进镜像,是“连锁中央厨房”式的一键还原。训练加速、报告生成、参数压缩都不是 Docker 的职责。
Q5. 关于复现性与稳健性的关系,正确的是?
- A.复现性等同于模型的鲁棒性能
- B.复现性要求相同条件结果一致← 正确答案
- C.稳健性要求相同条件相同结果
- D.两者关注的场景完全相同
答案:B。复现性关注“相同条件下结果一致”,稳健性关注“条件变化时结果依然稳定”。A 把复现等同于鲁棒,C 把稳健性描述成复现性,D 说两者相同,均错误。
Q6. 实现完整复现需要包含哪些要素?
- A.精确锁定代码的版本号← 正确答案
- B.记录依赖库的环境快照← 正确答案
- C.固定随机种子并记录它← 正确答案
- D.追求更高的硬件配置
答案:ABC。完整复现需要代码、环境、种子三者齐备,如同菜谱要写明“食材、设备、水源”。硬件配置高低不决定是否复现,但硬件型号差异需要记录,故 D 不选。
Q7. requirements.txt 的核心作用是?
- A.描述模型网络的结构
- B.锁定 Python 依赖版本← 正确答案
- C.存放训练好的权重
- D.记录数据标注的格式
答案:B。requirements.txt 记录依赖库名称与版本号,是“厨房设备清单”。模型结构、权重、标注格式都不属于依赖清单的职责。
Q8. 以下哪些因素会导致复现失败?
- A.依赖库发生版本漂移← 正确答案
- B.数据划分的顺序变化← 正确答案
- C.使用非确定性并行算法← 正确答案
- D.仅修改变量名的拼写方式
答案:ABC。依赖漂移、数据划分顺序变化、非确定性算法都会改变实验输入或计算路径。仅修改变量名不影响程序逻辑与数值结果,故 D 不会导致复现失败。
Q9. 关于 GPU 的非确定性,正确的说法是?
- A.只要固定种子结果就完全确定
- B.并行归约顺序可能带来微小差异← 正确答案
- C.一切 GPU 训练都无法实验复现
- D.非确定性只来自数据增强环节
答案:B。GPU 并行归约顺序不固定会带来微小浮点差异。A 过于绝对(种子固定不能消除并行非确定性),C 过于悲观,D 把非确定性来源限定为数据增强也错误。
Q10. 复现验证流程通常包含哪些步骤?
- A.记录原始实验的基准指标← 正确答案
- B.在同样的环境里重跑实验← 正确答案
- C.对比指标误差是否在阈值内← 正确答案
- D.把模型直接部署到线上环境
答案:ABC。复现验证的完整流程是“记录基准 → 重跑实验 → 对比判定”,对应公式中的基准指标、复现指标与相对误差阈值。部署上线属于生产环节,不属于验证流程。