推理输入契约
预测输入必须与训练时的列名、顺序、单位、类型、缺失约定和二维形状一致。
学习前需要掌握
背景与问题
模型接收的数组即使列数正确,也可能把年龄读成金额,或把元读成万元。预测接口需要检查结构与业务含义,不能靠模型猜测列名、单位和缺失值约定。
契约应随模型版本保存,并在进入预处理前验证。训练允许的缺失值、类别范围和数据类型,应与推理时采用同一约定。
概念定义
输入契约是模型接受数据的明确结构与语义约束,不只是数组长度。
直观理解
模型只认识训练时那套表头和单位,不能靠猜测理解新格式。
核心原理
结构检查包含二维形状、必需列、重复列和额外字段。带列名 DataFrame 可以先验证列集合,再按契约显式排序;不能对缺失列静默补零,或按当前恰好出现的顺序直接转数组。
语义检查包含单位、类型、范围和时间含义。年龄与金额都可以是浮点数,不能仅凭数值 dtype 判断正确;单位需要来自可信数据接口或明确转换流程,不能从每个数值可靠推断。
缺失值策略必须与 Pipeline 一致。若训练使用填补器,可以允许约定的缺失表示,但仍拒绝无穷值和错误类型;若契约要求完整数值,就应在预测前清楚报错,而不是让不同接口各自填值。
单条样本也应保持一行多列,避免 DataFrame 取成 Series 后降为一维。校验成功只代表输入在已定义范围内,不代表分布没有漂移或预测必然正确;还应记录契约与模型版本便于排查。
数学表达
核心表达
优先使用带列名 DataFrame;在 predict 前验证列集合、顺序、有限值、范围与模型版本。
变量含义
变量定义与本页概念定义一致,代码中的同名变量保持相同语义。
计算步骤
- 1保存 expected_columns
- 2检查缺失/多余列
- 3重排列顺序
- 4验证 dtype/单位/有限性
代码对应位置
示例 1完整验证一条数值输入并拒绝单位不符:本例采用无缺失、非负金额与有限年龄的显式契约,只验证输入,不假装完成模型预测。
案例:金额与年龄列换位时先对齐
约定输入列为 age_years、amount_yuan,单位分别是年与元,并且本例不允许缺失。请求按相反列顺序提供时,可以在核对同一列集合后安全重排;若金额实际上是分,必须先走明确单位转换或拒绝。
下方验证器会拒绝重复列、未知列、非数值、非有限值和年龄范围错误。单位来自显式元数据,这仍要求上游如实提供;验证器不能通过观察数字“自动知道”元与分是否弄反。
计算与实现步骤
- 保存 expected_columns
- 检查缺失/多余列
- 重排列顺序
- 验证 dtype/单位/有限性
- 保留二维
- 记录版本
代码实现
示例 1
完整验证一条数值输入并拒绝单位不符
example_01.py用途:本例采用无缺失、非负金额与有限年龄的显式契约,只验证输入,不假装完成模型预测。
import numpy as np
import pandas as pd
EXPECTED = ["age_years", "amount_yuan"]
UNITS = {"age_years": "years", "amount_yuan": "yuan"}
def validate(frame, units):
if frame.columns.duplicated().any() or set(frame.columns) != set(EXPECTED):
raise ValueError("column mismatch")
if units != UNITS:
raise ValueError("unit mismatch")
aligned = frame.loc[:, EXPECTED]
if not all(pd.api.types.is_numeric_dtype(dtype) for dtype in aligned.dtypes):
raise TypeError("numeric columns required")
if not np.isfinite(aligned.to_numpy(dtype=float)).all():
raise ValueError("finite values required")
if not aligned["age_years"].between(0, 120).all():
raise ValueError("age outside contract")
if (aligned["amount_yuan"] < 0).any():
raise ValueError("negative amount")
return aligned
sample = pd.DataFrame({"amount_yuan": [100.0], "age_years": [24]})
validated = validate(sample, UNITS)
print("columns:", validated.columns.tolist())
print("shape:", validated.shape)
try:
validate(sample, {"age_years": "years", "amount_yuan": "cents"})
except ValueError as error:
print("rejected:", error)代码解析
解析始终位于完整代码下方,并按实际代码段逐项对应。
输入数据与任务
本例采用无缺失、非负金额与有限年龄的显式契约,只验证输入,不假装完成模型预测。
Step 1 · 1–2 行
导入当前步骤需要的数值计算、预处理、模型或评价工具。依赖集中写在代码开头,便于复现。
import numpy as np
import pandas as pdStep 2 · 4–5 行
单位与缺失策略是业务契约,不能从 dtype 自动推断。
EXPECTED = ["age_years", "amount_yuan"]
UNITS = {"age_years": "years", "amount_yuan": "yuan"}Step 3 · 7–21 行
执行当前代码段,并把得到的状态传给下一步。
def validate(frame, units):
if frame.columns.duplicated().any() or set(frame.columns) != set(EXPECTED):
raise ValueError("column mismatch")
if units != UNITS:
raise ValueError("unit mismatch")
aligned = frame.loc[:, EXPECTED]
if not all(pd.api.types.is_numeric_dtype(dtype) for dtype in aligned.dtypes):
raise TypeError("numeric columns required")
if not np.isfinite(aligned.to_numpy(dtype=float)).all():
raise ValueError("finite values required")
if not aligned["age_years"].between(0, 120).all():
raise ValueError("age outside contract")
if (aligned["amount_yuan"] < 0).any():
raise ValueError("negative amount")
return alignedStep 4 · 23–30 行
输出中间参数、形状或最终指标,用于核对代码是否符合预期。
sample = pd.DataFrame({"amount_yuan": [100.0], "age_years": [24]})
validated = validate(sample, UNITS)
print("columns:", validated.columns.tolist())
print("shape:", validated.shape)
try:
validate(sample, {"age_years": "years", "amount_yuan": "cents"})
except ValueError as error:
print("rejected:", error)预期输出或运行结果
本地 scikit-learn 1.8.0 的核对输出: columns: ['age_years', 'amount_yuan'] shape: (1, 2) rejected: unit mismatch
常见错误 · 4 条
- 只检查列数或总元素数,不检查名称、重复列、单位和排列。
- 静默把未知字符串转数字、把缺失列填零,改变训练时的输入语义。
- 在允许填补的契约中一律拒绝 NaN,或在要求完整输入的契约中随意填补。
- 确认样本轴、特征轴以及 X 与 y 的第一维完全对应。
实际应用
- 批量与单条预测
- API 输入校验
常见错误
- 只检查列数或总元素数,不检查名称、重复列、单位和排列。
- 静默把未知字符串转数字、把缺失列填零,改变训练时的输入语义。
- 在允许填补的契约中一律拒绝 NaN,或在要求完整输入的契约中随意填补。
推荐学习资料
参考库不会生成虚假资源或无效外部链接。