1. 还原内核仍然知道的内容
notebook 文件与内核状态并不总是讲述同一个故事。一个变量可能来自已删除的单元格,一个列表可能被多次修改,而一个在最后一次代码更改之前加载的对象可能仍留在内存中。因此,显示的结果并不能证明当前单元格按可见顺序仍能产生这些结果。
保留一份工作副本,重启内核,然后从头到尾执行各个单元格。记下第一个失败或结果发生变化的单元格。查找缺失的依赖项,而不是从旧会话中手动重新注入一个变量。Jupyter 内核是一个独立进程;关闭标签页并不等同于重建一个全新的环境。
还要清点外部影响:下载、安装包、更改目录、读取已生成的文件以及使用环境变量。一个看起来即时得出结果的单元格,可能只是复用了旧文件。你的未来脚本必须能够区分有意提供的输入和试验的残留物。
2. 在迁移代码之前先写好契约
只选择一个要抽取的任务。例如:读取一个分数文件,保留分数达到阈值的标识符并写出结果。本指南中的示例仅用于教学,不实际执行,也不使用 GPU。它的作用是展示一次执行的依赖关系,而不是宣称某项测量结果或随 Kernodeck 提供的工具。
请足够精确地定义输入、参数和输出,以便验证这一转换。这里的阈值是包含式的:分数等于 0.5 也会被保留。标识符必须始终与各自的分数关联,且保持输入顺序。不得因新的试验而无意中覆盖已有的输出。
这一步可避免含糊不清的迁移:如果 notebook 会剔除等于阈值的分数,而脚本却保留它们,那么你就改变了计算逻辑。请明确判定这是一次修正还是一次回归。要保留一个恰好位于边界上的用例,而不仅仅是两个相差很远的值。
左右滚动表格即可查看所有列。| 元素 | 示例取值 | 判定标准 |
|---|---|---|
| 输入 | a:0.4;b:0.8;c:0.5 | 三个不同的标识符,分数已确认介于 0 和 1 之间。 |
| 参数 | 阈值 0.5 | 大于或等于比较。 |
| 预期输出 | b,然后是 c | 两个标识符,无重复,不重新排序。 |
3. 抽取一个不再依赖某个单元格的函数
将转换与读写操作分开。计算函数接收其数据和阈值,然后返回被选中的标识符。它不查询名为 seuil 的全局变量,不隐式打开文件,也不修改输入列表。这样一来,notebook 和脚本就能调用完全相同的计算逻辑。
在代码片段中,数据假定已按照前面的契约完成验证。因此该函数并不构成完整的文件校验器。这一限制是有意为之:请在程序入口处检查格式,然后让转换逻辑保持易于理解。添加一个参数不应要求你再去翻找那个曾修改过某个值的单元格。
notebook 可以继续作为你的探索工具。让它导入这个函数,而不是维护第二份副本。修改模块后,请从全新的内核开始,以便对比两条路径;一个早已导入的旧函数不应干扰验证。
def retenir_identifiants(records, seuil):
return [
record["id"]
for record in records
if record["score"] >= seuil
]
if __name__ == "__main__":
records = [
{"id": "a", "score": 0.4},
{"id": "b", "score": 0.8},
{"id": "c", "score": 0.5},
]
attendu = ["b", "c"]
obtenu = retenir_identifiants(records, 0.5)
if obtenu != attendu:
raise SystemExit("Sélection inattendue")4. 让参数成为可见的输入
脚本的入口点负责处理参数、验证各项选择并调用各个函数。标准库模块 argparse 用于描述选项并生成帮助信息;它并不了解你的业务规则。一个在语法上被接受的浮点数,仍可能超出允许的范围。因此,本示例中的阈值需要额外检查。
明确路径解析的基准:是相对于命令启动时所在的目录,还是相对于显式选择的项目文件夹。不要在计算过程中隐式切换目录。下面的代码块仅展示参数解析;数据读取与写入仍需在读取端程序中自行接入。
将密钥排除在这些参数之外。可共享的参数用于描述实验本身;仓库或存储的访问凭据应通过其他渠道传递。一条能发给同事的有用命令,应当可以复制而不必连带复制令牌。
import argparse
from pathlib import Path
def lire_arguments():
parser = argparse.ArgumentParser()
parser.add_argument("--input", required=True, type=Path)
parser.add_argument("--output", required=True, type=Path)
parser.add_argument("--seuil", required=True, type=float)
args = parser.parse_args()
if not 0 <= args.seuil <= 1:
parser.error("Le seuil doit être compris entre 0 et 1.")
return args5. 为脚本提供明确的终结和可验证的输出
将编排逻辑放入 main 函数,并在 if __name__ == "__main__" 条件下触发。这样模块就可以被 Notebook 导入而不会立即启动处理。导入语句定义工具;主入口决定何时读取、计算和写入。
为每次运行分配独立的文件夹。保存实际使用的非敏感参数和输入标识,然后写入结果。对于我们的筛选,请核对标识数量、它们是否属于输入集,以及阈值规则。格式良好的 JSON 文件也可能包含错误的标识;仅有文件本身并不足够。
如果输入文件缺失或目标位置不可用,应显式失败。不要用空列表来代替这些问题:空列表可能被解读为有效的筛选结果。程序必须区分“没有结果满足阈值”和“因读取失败而没有结果”。
6. 在两次全新运行中进行比较
首先使用这三行教学数据。阈值为 0.5 时,预期得到 b 和 c;为 0.9 时,预期得到空列表;为 0.4 时,预期得到三个标识。这些答案可从约定推导出来,并非此处实际运行的结果。它们有助于发现比较运算符写反或顺序错误。
然后在全新进程中,用相同的输入运行重新启动的 Notebook 和脚本。比较其中有用的值,而不是截图或文件中记录的时间。再加入第二组有代表性的数据集和一个无效输入。记录预期中的差异,例如输出呈现得更为朴素。
nbconvert 转换可以加快单元格的初步迁移,但其魔法命令可能仍依赖 Jupyter。请移除或替换 Notebook 特有的指令、无用的输出以及临时安装。导出只是起点;从全新状态进行比较才能决定迁移是否完成。
7. 迁移到 GPU,而不重新引入隐藏状态
在理解 CPU 流程之后,将模型加载和后端接入同样的显式结构。保留版本、精度、输入和输出位置。迁移到 GPU 既不能修正单元格顺序不一致的问题,也不能修正由旧尝试生成的文件。请单独验证 PyTorch 确实能在所选设备上执行计算。
如果两次运行产生不同的值,请区分被遗忘的状态、随机源以及计算的数值精度限制。随机种子并不能普遍保证在不同版本和硬件之间结果一致。对于中断的训练,请使用专门的检查点流程:本指南转换的是入口点,而不是重建优化器或生成器的状态。
有用的产出是一个可以用一条命令描述的程序,包括其前置条件和结果校验。Notebook 仍可自由用于探索和绘图;它不再独自承载“计算该如何启动”这一记忆。