Python 现代开发学习笔记

(十七)先完成目录,再发布版本

1. 区分完整版本与默认指向

# 续写同一文件:model_project/src/modeldelivery/releases.py
def activate(root, version):
    # 验证完整版本后替换指向文件,已有 Predictor 不受这个动作影响。
    root = Path(root).resolve()
    verify(version_path(root, version))
    temporary = root / "current.json.tmp"
    write_json(temporary, {"version": version})
    os.replace(temporary, root / "current.json")

activate() 只改 current.json,先验证目标版本,再以临时文件替换指向。已经发布的 v1/ 不改写;训练新的结果用 v2/。这样检查旧版本、回退和复现预测时,都有明确的文件位置。

2. 在临时目录写完整套文件

# 续写同一文件:model_project/src/modeldelivery/releases.py
def publish(csv, root, version, epochs=35, seed=7):
    root = Path(root).resolve()
    root.mkdir(parents=True, exist_ok=True)
    destination = version_path(root, version)
    if destination.exists():
        raise ValueError("这个版本已经存在,请选择新的版本名")
    prepared = prepare(csv, seed)
    weights, metrics, history = fit(prepared, epochs, seed)
    with TemporaryDirectory(prefix=".build-", dir=root) as folder:
        stage = Path(folder).resolve()
        # 两个绝对路径都在指定模型根目录内,再发布完整目录。
        if stage.parent != root or destination.parent != root:
            raise ValueError("模型发布路径不在根目录内")
        torch.save(weights, stage / "weights.pt")
        np.savez(stage / "preprocess.npz", **prepared.scaler)

训练结束后,在模型根目录内部创建 .build-... 临时目录。权重与缩放先写到这里;模型服务仍能继续读取先前完整版本。TemporaryDirectory 管理这次临时目录,写入失败后清理尚未发布的内容。

# 续写同一文件:model_project/src/modeldelivery/releases.py
        # 将解释模型所需的元数据与同次训练记录一起保存。
        metadata = {
            "format": 1,
            "version": version,
            "features": FEATURES,
            "architecture": "linear-2-1",
            "source_hash": prepared.source_hash,
            "seed": seed,
            "epochs": epochs,
            "torch_version": str(torch.__version__),
            "numpy_version": str(np.__version__),
        }
        write_json(stage / "metadata.json", metadata)
        write_json(stage / "metrics.json", metrics)
        write_json(stage / "history.json", history)
        write_json(
            stage / "splits.json",
            {name: ids.tolist() for name, ids in prepared.splits.items()},
        )

元数据保存版本名、数据摘要、随机种子、NumPy 与 PyTorch 版本。历史和分组下标与权重一起输出,之后复查选中轮数时能找到同次实验的记录。

3. 最后才改变加载入口

# 续写同一文件:model_project/src/modeldelivery/releases.py
        # 清单写完并通过重算检查后,才发布正式目录。
        write_json(
            stage / "manifest.json",
            {
                "format": 1,
                "files": {
                    name: hashlib.sha256((stage / name).read_bytes()).hexdigest()
                    for name in sorted(FILES)
                },
            },
        )
        verify(stage)
        stage.rename(destination)
    activate(root, version)  # 最后才改变“下次启动加载哪一版”的指向。
    return {"version": version, **metrics}

写完六个文件,计算清单并重新核对;通过后将临时目录改名为正式版本。两者都在同一模型根目录下,这一步不跨磁盘搬运文件。只有完整版本已经存在,才调用 activate()。在保存数组时故意抛错的测试会检查:没有半份 v2,当前指向仍是 v1。

新文件先写入临时目录,核对后改名为 v2,再替换 current;中途失败时保留旧指向

发布命令按顺序执行。先完成一次训练并检查报告,再决定激活与重开服务;同一个版本目录保存同一次发布的内容。

笔记正文与全部题解 · Ctrl / ⌘ K