Python 现代开发学习笔记

(十八)重放预处理,再调用同版权重

1. 装载时核对文件与解释方式

# 项目文件:model_project/src/modeldelivery/predict.py
import json
from pathlib import Path

import numpy as np
import torch

from .data import FEATURES
from .model import ReadingRegressor
from .releases import verify, version_path


class Predictor:
    def __init__(self, root, version=None):
        # 先选完整版本,再读取同版缩放与权重,关闭数组文件后仍保留副本。
        root = Path(root).resolve()
        version = (
            version
            or json.loads((root / "current.json").read_text(encoding="utf-8"))[
                "version"
            ]
        )
        folder = version_path(root, version)
        verify(folder)
        self.metadata = json.loads(
            (folder / "metadata.json").read_text(encoding="utf-8")
        )
        if (
            self.metadata["features"] != FEATURES
            or self.metadata["architecture"] != "linear-2-1"
        ):
            raise ValueError("特征顺序或模型结构与程序不一致")
        self.version = version
        with np.load(folder / "preprocess.npz", allow_pickle=False) as archive:
            self.scaler = {name: archive[name].copy() for name in archive.files}
        self.model = ReadingRegressor()
        self.model.load_state_dict(
            torch.load(folder / "weights.pt", map_location="cpu", weights_only=True)
        )
        self.model.eval()

没有显式版本时,读取 current.json;指定版本时直接选择相应目录。先检查摘要,再检查特征顺序和结构。np.load(..., allow_pickle=False) 只读取数组,关闭文件前复制进内存。torch.load(..., weights_only=True, map_location="cpu") 读取权重,接着放入同样结构并切到 eval()。

2. 跟踪一条请求的形状与单位

# 续写同一文件:model_project/src/modeldelivery/predict.py
    def pages(self, minutes, sessions):
        # 特征顺序由交付元数据约定,NumPy 与训练使用相同转换。
        x = np.asarray([[minutes, sessions]], dtype=np.float32)
        if not np.isfinite(x).all() or (x <= 0).any():
            raise ValueError("分钟数和次数需要有限正数")
        standardized = ((x - self.scaler["x_mean"]) / self.scaler["x_scale"]).astype(
            np.float32
        )
        with torch.inference_mode():
            prediction = self.model(torch.from_numpy(standardized)).numpy()
        pages = prediction * self.scaler["y_scale"] + self.scaler["y_mean"]
        return float(pages.item())

输入 minutes=35、sessions=3,先得到 [[35,3]],形状 (1,2)。逐列减去训练均值、除以训练标准差,再转 Tensor。模型输出形状 (1,1);乘 y_scale、加 y_mean 之后,才得到页数。

逐步显示字段顺序、1×2 数组、标准化 Tensor、1×1 输出与还原页数,保留同版预处理参数

这里不重新对单条请求求均值和标准差。单行自己求均值会把输入解释方式改掉,权重却仍是按训练组缩放学习的。同样,若把次数和分钟数对调,数组形状虽然仍正确,含义已经改变;特征顺序检查就是为此保留的。

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