Files

157 lines
6.4 KiB
Python
Raw Permalink Normal View History

2026-07-03 22:11:35 +08:00
"""
model_core/vocab.py -- Formula_Vocabulary 集成与确定性版本(R3)
本模块把 Formula_Vocabulary 从「手工维护的特征名元组 + 手工版本字符串」迁移为
由注册层(`model_core.registry.Registry`)驱动、版本确定性派生的实现:
- `feature_names` 来自 `features.FEATURE_REGISTRY`(有序)。
- `operator_names` 来自 `ops.OPERATOR_REGISTRY`(有序)。
- token id 分段:feature id ∈ [0, F-1],operator id ∈ [F, F+O-1],两段严格
不相交(`operator_offset == feature_count`,R3.3)。
- 构建时用集合校验 token 名称全局唯一、无缺失/重复/多余(R3.1、R3.2)。
- `VOCAB_VERSION` 由有序 token 名称列表确定性派生(R3.4、R3.5):
VOCAB_VERSION = "v" + sha256("\n".join(token_names)).hexdigest()[:12]
相同有序列表 → 相同版本;任意组成/顺序变化 → 不同版本。
- `FORMULA_VOCAB.verify(artifact_version)`:版本不匹配抛
`VocabVersionMismatchError`,拒绝且不消费任何 token(R3.7)。
- `VOCAB_SCHEMA_TAG`:人类可读的 schema 标签,仅供日志展示,不参与兼容判定。
import 方向说明:`features.py` / `ops.py` 只依赖 `.registry`,本模块从二者读取
2026-07-10 18:14:59 +08:00
注册表视图不构成循环依赖。下游 `vm.py` / `config.py` /
2026-07-03 22:11:35 +08:00
`alphagpt.py` / `engine.py` 对 `FEATURE_NAMES` / `FORMULA_VOCAB` / `VOCAB_VERSION`
的 import 保持兼容。
"""
from __future__ import annotations
import hashlib
2026-07-02 00:51:15 +08:00
from dataclasses import dataclass
2026-07-03 22:11:35 +08:00
from .features import FEATURE_REGISTRY
from .ops import OPERATOR_REGISTRY
2026-07-02 00:51:15 +08:00
2026-07-03 22:11:35 +08:00
# 人类可读 schema 标签(仅供日志/报告展示,不参与兼容性判定,R3.5)
VOCAB_SCHEMA_TAG = "4.0-registry"
2026-07-03 01:11:00 +08:00
2026-07-02 00:51:15 +08:00
2026-07-03 22:11:35 +08:00
# ── 版本层异常(R3.7)───────────────────────────────────────────────────
class VocabVersionMismatchError(Exception):
"""加载产物版本 ≠ 当前派生 VOCAB_VERSION(R3.7)。
由 `FORMULA_VOCAB.verify()` 在版本不匹配时抛出;调用方应拒绝加载且不消费
任何 token。
"""
# ── 确定性版本派生(R3.4、R3.5)─────────────────────────────────────────
def compute_vocab_version(token_names: tuple[str, ...]) -> str:
"""由有序 token 名称列表确定性派生紧凑版本标识。
VOCAB_VERSION = "v" + sha256("\n".join(token_names)).hexdigest()[:12]
性质:相同的有序列表 → 相同版本;任意组成或顺序变化 → 不同版本。使用换行
作为稳定分隔符,避免名称拼接歧义。
"""
joined = "\n".join(token_names)
digest = hashlib.sha256(joined.encode("utf-8")).hexdigest()
return "v" + digest[:12]
2026-07-02 00:51:15 +08:00
@dataclass(frozen=True)
class FormulaVocab:
feature_names: tuple[str, ...]
operator_names: tuple[str, ...]
@property
def feature_count(self) -> int:
return len(self.feature_names)
@property
def operator_offset(self) -> int:
2026-07-03 22:11:35 +08:00
# feature id 段 [0, F-1]、operator id 段 [F, F+O-1] 严格不相交(R3.3)
2026-07-02 00:51:15 +08:00
return self.feature_count
@property
def token_names(self) -> tuple[str, ...]:
return self.feature_names + self.operator_names
@property
def size(self) -> int:
return len(self.token_names)
2026-07-03 22:11:35 +08:00
@property
def version(self) -> str:
"""当前词表组成确定性派生的 VOCAB_VERSION(R3.4)。"""
return compute_vocab_version(self.token_names)
2026-07-02 00:51:15 +08:00
2026-07-03 22:11:35 +08:00
def verify(self, artifact_version: str) -> None:
"""校验产物版本与当前派生版本一致(R3.7)。
不匹配抛 `VocabVersionMismatchError`,调用方据此拒绝加载、不消费任何
token。匹配则静默返回。
"""
current = self.version
if artifact_version != current:
raise VocabVersionMismatchError(
f"词表版本不匹配:产物版本 {artifact_version!r} != "
f"当前派生版本 {current!r};旧 checkpoint / best_strategy.json "
f"需重新训练/重建后加载"
)
# ── 构建 FORMULA_VOCAB(由 registry 派生)与完整性校验(R3.1、R3.2)──────
def _build_formula_vocab() -> FormulaVocab:
"""由 FEATURE_REGISTRY / OPERATOR_REGISTRY 构建词表并做完整性校验。
校验(用集合,R3.1、R3.2):
- feature / operator 名称各自无重复;
- feature 与 operator 名称跨段全局唯一(无交集);
- size == F + O(无缺失/多余)。
任一校验失败即抛错,不产出不一致的词表。
"""
feature_names = tuple(FEATURE_REGISTRY.feature_names)
operator_names = tuple(OPERATOR_REGISTRY.operator_names)
feat_set = set(feature_names)
op_set = set(operator_names)
# 段内唯一
if len(feat_set) != len(feature_names):
dup = sorted({n for n in feature_names if feature_names.count(n) > 1})
raise ValueError(f"feature 名称存在重复: {dup}")
if len(op_set) != len(operator_names):
dup = sorted({n for n in operator_names if operator_names.count(n) > 1})
raise ValueError(f"operator 名称存在重复: {dup}")
# 跨段全局唯一(feature/operator 名称不得冲突)
overlap = feat_set & op_set
if overlap:
raise ValueError(f"feature 与 operator 名称冲突: {sorted(overlap)}")
vocab = FormulaVocab(feature_names=feature_names, operator_names=operator_names)
# 计数一致性:size == F + O,无缺失/重复/多余(R3.2)
expected = len(feature_names) + len(operator_names)
if vocab.size != expected:
raise ValueError(
f"词表计数不一致: size={vocab.size} != F+O={expected}"
)
# 全局 token 名称唯一(无缺失/重复/多余)
if len(set(vocab.token_names)) != vocab.size:
raise ValueError("token 名称存在重复或缺失,词表完整性校验失败")
return vocab
FORMULA_VOCAB = _build_formula_vocab()
# 由注册表导出有序特征名视图(保持下游 import 兼容)
FEATURE_NAMES = FORMULA_VOCAB.feature_names
# 词表版本:由有序 token 名称列表确定性派生(R3.4、R3.5)。
# 特征/算子的组成或顺序变化都会改变本值,旧 checkpoint / best_strategy.json 将
# 因版本不匹配而被 verify() 拒绝加载。
VOCAB_VERSION = FORMULA_VOCAB.version