Download src/pcm/planner/split_translator.py from Dermitio/Planner-Cache: direct link, hf CLI and curl.
- Browser
- Download file 16.2 kB
-
https://huggingface.co/Dermitio/Planner-Cache/resolve/main/src/pcm/planner/split_translator.py
- Command line
-
hf download hf://Dermitio/Planner-Cache/src/pcm/planner/split_translator.py
-
curl -L -o split_translator.py https://huggingface.co/Dermitio/Planner-Cache/resolve/main/src/pcm/planner/split_translator.py
16.2 kB
| """Split model-to-canonical routing and canonical-to-model translation.""" | |
| from __future__ import annotations | |
| from dataclasses import asdict, dataclass | |
| import json | |
| from pathlib import Path | |
| import torch | |
| from torch import Tensor, nn | |
| import torch.nn.functional as F | |
| from safetensors import safe_open | |
| from safetensors.torch import load_file, save_file | |
| from pcm.planner.canonical import CanonicalPStore | |
| from pcm.planner.canonical import ( | |
| CANONICAL_P_PROTOCOL, | |
| model_config_checksum, | |
| tensor_state_checksum, | |
| ) | |
| from pcm.planner.cache import Freshness | |
| SPLIT_TRANSLATE_FORMAT = "pcm-split-translate-v1" | |
| CANONICAL_ROUTER_FORMAT = "pcm-canonical-router-v1" | |
| def tensor_checksum(state: dict[str, Tensor]) -> str: | |
| return tensor_state_checksum(state) | |
| class ByteEntityEncoder(nn.Module): | |
| """Tokenizer-independent signed byte n-gram features for open entity names.""" | |
| def __init__(self, width: int = 128) -> None: | |
| super().__init__() | |
| if width < 32: | |
| raise ValueError("byte entity width must be at least 32") | |
| self.width = width | |
| def encode_one(self, surface: str) -> Tensor: | |
| data = surface.strip().casefold().encode("utf-8") | |
| if not data: | |
| raise ValueError("entity surface form cannot be empty") | |
| vector = torch.zeros(self.width, dtype=torch.float32) | |
| for position, byte in enumerate(data): | |
| vector[(byte * 17 + position * 31) % self.width] += 1.0 | |
| for position, (left, right) in enumerate(zip(data, data[1:])): | |
| bucket = (left * 257 + right * 17 + position * 13) % self.width | |
| sign = 1.0 if ((left + right + position) & 1) == 0 else -1.0 | |
| vector[bucket] += 0.5 * sign | |
| return F.normalize(vector, dim=0) | |
| def forward(self, surfaces: list[str] | tuple[str, ...]) -> Tensor: | |
| return torch.stack([self.encode_one(surface) for surface in surfaces]) | |
| class FactorizedCanonicalQuery: | |
| entity: Tensor | |
| relation_logits: Tensor | |
| metadata_logits: Tensor | |
| class ModelToCanonicalQueryProjector(nn.Module): | |
| def __init__( | |
| self, | |
| model_hidden_width: int, | |
| *, | |
| entity_width: int = 128, | |
| relation_count: int = 3, | |
| metadata_count: int = 4, | |
| ) -> None: | |
| super().__init__() | |
| self.model_hidden_width = model_hidden_width | |
| self.entity_width = entity_width | |
| self.relation_count = relation_count | |
| self.metadata_count = metadata_count | |
| self.norm = nn.LayerNorm(model_hidden_width) | |
| self.shared = nn.Linear(model_hidden_width, 512) | |
| self.entity_head = nn.Linear(512, entity_width) | |
| self.relation_head = nn.Linear(512, relation_count) | |
| self.metadata_head = nn.Linear(512, metadata_count) | |
| def forward(self, hidden: Tensor, entity_anchor: Tensor | None = None) -> FactorizedCanonicalQuery: | |
| hidden = hidden.detach().to(self.shared.weight.dtype) | |
| features = F.gelu(self.shared(self.norm(hidden))) | |
| predicted_entity = F.normalize(self.entity_head(features), dim=-1) | |
| if entity_anchor is not None: | |
| predicted_entity = F.normalize( | |
| entity_anchor.to(device=hidden.device, dtype=predicted_entity.dtype), dim=-1 | |
| ) | |
| return FactorizedCanonicalQuery( | |
| entity=predicted_entity, | |
| relation_logits=self.relation_head(features), | |
| metadata_logits=self.metadata_head(features), | |
| ) | |
| class FrozenLexicalAnchorProjector(nn.Module): | |
| """Experimental model-native lexical anchor mapped into canonical entity space.""" | |
| def __init__(self, model_hidden_width: int, entity_width: int = 128) -> None: | |
| super().__init__() | |
| self.norm = nn.LayerNorm(model_hidden_width) | |
| self.projection = nn.Sequential( | |
| nn.Linear(model_hidden_width, 256), nn.GELU(), nn.Linear(256, entity_width) | |
| ) | |
| def forward(self, lexical_hidden: Tensor) -> Tensor: | |
| return F.normalize(self.projection(self.norm(lexical_hidden.detach().float())), dim=-1) | |
| class RouterConfig: | |
| entity_width: int = 128 | |
| relation_count: int = 3 | |
| metadata_count: int = 4 | |
| format: str = CANONICAL_ROUTER_FORMAT | |
| canonical_protocol: str = CANONICAL_P_PROTOCOL | |
| architecture: str = "canonical_factor_router_v1" | |
| class CanonicalRouterIndex: | |
| entity: Tensor | |
| relation_id: Tensor | |
| metadata_id: Tensor | |
| valid: Tensor | |
| class RouteResult: | |
| indices: Tensor | |
| scores: Tensor | |
| weights: Tensor | |
| features: Tensor | |
| accepted: Tensor | |
| has_valid: bool | |
| class CanonicalPRouter(nn.Module): | |
| """Universal canonical-only scorer; it has no model-hidden dimensions.""" | |
| def __init__(self, config: RouterConfig = RouterConfig()) -> None: | |
| super().__init__() | |
| self.config = config | |
| self.scorer = nn.Linear(4, 1) | |
| self.register_buffer("acceptance_threshold", torch.tensor(0.0)) | |
| with torch.no_grad(): | |
| self.scorer.weight.copy_(torch.tensor([[8.0, 4.0, 2.0, 2.0]])) | |
| self.scorer.bias.zero_() | |
| def build_index( | |
| self, | |
| store: CanonicalPStore, | |
| encoder: ByteEntityEncoder, | |
| *, | |
| device: str | torch.device, | |
| ) -> CanonicalRouterIndex: | |
| surfaces = [] | |
| for valid, label in zip(store.valid.tolist(), store.cache.labels): | |
| if valid and not label: | |
| raise ValueError("routable canonical P slots require an entity surface label") | |
| surfaces.append(label if label else "<invalid>") | |
| entity = encoder(surfaces).to(device) | |
| return CanonicalRouterIndex( | |
| entity=entity, | |
| relation_id=store.relation_id.to(device), | |
| metadata_id=store.canonical_metadata_id.to(device), | |
| valid=( | |
| store.valid | |
| & (store.cache.freshness != int(Freshness.STALE)) | |
| ).to(device), | |
| ) | |
| def all_scores( | |
| self, query: FactorizedCanonicalQuery, index: CanonicalRouterIndex | |
| ) -> tuple[Tensor, Tensor]: | |
| entity = torch.einsum("...d,sd->...s", query.entity.float(), index.entity.float()) | |
| relation_probability = F.softmax(query.relation_logits.float(), dim=-1) | |
| relation_ids = index.relation_id.clamp_min(0) | |
| relation = relation_probability[..., relation_ids] | |
| metadata_probability = F.softmax(query.metadata_logits.float(), dim=-1) | |
| metadata_ids = index.metadata_id.clamp_min(0) | |
| metadata = metadata_probability[..., metadata_ids] | |
| current = (index.metadata_id == 0).float().view( | |
| *((1,) * (entity.ndim - 1)), -1 | |
| ).expand_as(entity) | |
| features = torch.stack((entity, relation, metadata, current), dim=-1) | |
| scores = self.scorer(features).squeeze(-1) | |
| valid = index.valid.view(*((1,) * (scores.ndim - 1)), -1) | |
| return scores.masked_fill(~valid, -torch.inf), features | |
| def route( | |
| self, | |
| query: FactorizedCanonicalQuery, | |
| index: CanonicalRouterIndex, | |
| *, | |
| top_k: int = 1, | |
| ) -> RouteResult: | |
| if top_k <= 0: | |
| raise ValueError("top_k must be positive") | |
| scores, features = self.all_scores(query, index) | |
| count = min(top_k, scores.shape[-1]) | |
| if not bool(index.valid.any()): | |
| shape = (*scores.shape[:-1], count) | |
| return RouteResult( | |
| indices=torch.zeros(shape, dtype=torch.long, device=scores.device), | |
| scores=torch.full(shape, -torch.inf, device=scores.device), | |
| weights=torch.zeros(shape, device=scores.device), | |
| features=torch.zeros((*shape, 4), device=scores.device), | |
| accepted=torch.zeros(scores.shape[:-1], dtype=torch.bool, device=scores.device), | |
| has_valid=False, | |
| ) | |
| selected_scores, indices = scores.topk(count, dim=-1) | |
| weights = torch.softmax(selected_scores, dim=-1) | |
| selected_features = features.gather( | |
| -2, indices.unsqueeze(-1).expand(*indices.shape, features.shape[-1]) | |
| ) | |
| return RouteResult( | |
| indices=indices, | |
| scores=selected_scores, | |
| weights=weights, | |
| features=selected_features, | |
| accepted=selected_scores[..., 0] >= self.acceptance_threshold, | |
| has_valid=bool(index.valid.any()), | |
| ) | |
| def calibrate_acceptance(self, positive_scores: Tensor, negative_scores: Tensor) -> float: | |
| positive_scores = positive_scores.detach().float().flatten() | |
| negative_scores = negative_scores.detach().float().flatten() | |
| candidates = torch.unique(torch.cat((positive_scores, negative_scores))).sort().values | |
| if candidates.numel() > 1: | |
| candidates = (candidates[:-1] + candidates[1:]) / 2 | |
| best_threshold = candidates[0] | |
| best_balanced = -1.0 | |
| for threshold in candidates: | |
| true_positive = (positive_scores >= threshold).float().mean() | |
| true_negative = (negative_scores < threshold).float().mean() | |
| balanced = float((true_positive + true_negative) / 2) | |
| if balanced > best_balanced: | |
| best_balanced = balanced | |
| best_threshold = threshold | |
| self.acceptance_threshold.copy_(best_threshold.to(self.acceptance_threshold.device)) | |
| return float(best_balanced) | |
| def save(self, path: str | Path) -> None: | |
| state = {name: value.detach().cpu() for name, value in self.state_dict().items()} | |
| save_file(state, str(path), metadata={ | |
| "format": CANONICAL_ROUTER_FORMAT, | |
| "config": json.dumps(asdict(self.config), sort_keys=True), | |
| "weights_sha256": tensor_checksum(state), | |
| }) | |
| def load(cls, path: str | Path, *, device="cpu") -> "CanonicalPRouter": | |
| with safe_open(str(path), framework="pt", device="cpu") as handle: | |
| metadata = handle.metadata() | |
| if metadata.get("format") != CANONICAL_ROUTER_FORMAT: | |
| raise ValueError("unsupported canonical router file") | |
| config = RouterConfig(**json.loads(metadata["config"])) | |
| result = cls(config).to(device) | |
| state = load_file(str(path), device=str(device)) | |
| if tensor_checksum(state) != metadata.get("weights_sha256"): | |
| raise ValueError("canonical router checksum does not match") | |
| result.load_state_dict(state) | |
| return result | |
| class CanonicalValueTranslator(nn.Module): | |
| def __init__(self, canonical_width: int, model_hidden_width: int) -> None: | |
| super().__init__() | |
| self.norm = nn.LayerNorm(canonical_width) | |
| self.input = nn.Linear(canonical_width, 512) | |
| self.output = nn.Linear(512, model_hidden_width) | |
| def forward(self, canonical: Tensor) -> Tensor: | |
| canonical = canonical.to(self.input.weight.dtype) | |
| return self.output(F.gelu(self.input(self.norm(canonical)))) | |
| class SplitInjectionGate(nn.Module): | |
| def __init__(self, model_hidden_width: int) -> None: | |
| super().__init__() | |
| self.hidden_norm = nn.LayerNorm(model_hidden_width) | |
| self.value_norm = nn.LayerNorm(model_hidden_width) | |
| self.joint = nn.Linear(model_hidden_width * 2 + 4, 64) | |
| self.output = nn.Linear(64, 1) | |
| nn.init.zeros_(self.output.weight) | |
| nn.init.constant_(self.output.bias, -4.0) | |
| def logits(self, hidden: Tensor, translated: Tensor, route_features: Tensor) -> Tensor: | |
| dtype = self.joint.weight.dtype | |
| joint = torch.cat(( | |
| self.hidden_norm(hidden.detach().to(dtype)), | |
| self.value_norm(translated.to(dtype)), | |
| route_features.to(dtype), | |
| ), dim=-1) | |
| return self.output(F.gelu(self.joint(joint))).squeeze(-1) | |
| def forward(self, hidden: Tensor, translated: Tensor, route_features: Tensor) -> Tensor: | |
| return torch.sigmoid(self.logits(hidden, translated, route_features)) | |
| class SplitTranslateConfig: | |
| model_id: str | |
| model_hidden_width: int | |
| attachment_layers: tuple[int, ...] | |
| canonical_width: int = 512 | |
| entity_width: int = 128 | |
| relation_count: int = 3 | |
| metadata_count: int = 4 | |
| canonical_protocol: str = CANONICAL_P_PROTOCOL | |
| format: str = SPLIT_TRANSLATE_FORMAT | |
| architecture: str = "split_query_value_joint_gate_v1" | |
| model_revision: str = "local" | |
| model_config_sha256: str = "unspecified" | |
| top_k: int = 1 | |
| def __post_init__(self): | |
| if self.format != SPLIT_TRANSLATE_FORMAT: | |
| raise ValueError("unsupported split translator format") | |
| if self.canonical_protocol != CANONICAL_P_PROTOCOL: | |
| raise ValueError("unsupported canonical P protocol") | |
| if self.model_hidden_width <= 0 or self.canonical_width <= 0: | |
| raise ValueError("translator widths must be positive") | |
| if not self.attachment_layers: | |
| raise ValueError("attachment layers cannot be empty") | |
| if self.top_k <= 0: | |
| raise ValueError("top_k must be positive") | |
| class SplitPTranslatePackage(nn.Module): | |
| """Model-specific query/value/gate modules; universal router is separate.""" | |
| def __init__(self, config: SplitTranslateConfig) -> None: | |
| super().__init__() | |
| self.config = config | |
| self.query_projector = ModelToCanonicalQueryProjector( | |
| config.model_hidden_width, | |
| entity_width=config.entity_width, | |
| relation_count=config.relation_count, | |
| metadata_count=config.metadata_count, | |
| ) | |
| self.value_translator = CanonicalValueTranslator( | |
| config.canonical_width, config.model_hidden_width | |
| ) | |
| self.gate = SplitInjectionGate(config.model_hidden_width) | |
| def validate_compatibility( | |
| self, | |
| *, | |
| model_id: str, | |
| model_hidden_width: int, | |
| canonical_protocol: str = CANONICAL_P_PROTOCOL, | |
| attachment_layers: tuple[int, ...] | None = None, | |
| model_config_sha256: str | None = None, | |
| ) -> None: | |
| errors = [] | |
| if model_id != self.config.model_id: | |
| errors.append("model identifier") | |
| if model_hidden_width != self.config.model_hidden_width: | |
| errors.append("model hidden width") | |
| if canonical_protocol != self.config.canonical_protocol: | |
| errors.append("canonical protocol") | |
| if attachment_layers is not None and tuple(attachment_layers) != self.config.attachment_layers: | |
| errors.append("attachment layers") | |
| if ( | |
| model_config_sha256 is not None | |
| and self.config.model_config_sha256 != "unspecified" | |
| and model_config_sha256 != self.config.model_config_sha256 | |
| ): | |
| errors.append("model config checksum") | |
| if errors: | |
| raise ValueError("incompatible split translator: " + ", ".join(errors)) | |
| def save(self, path: str | Path) -> None: | |
| state = {name: value.detach().cpu() for name, value in self.state_dict().items()} | |
| save_file(state, str(path), metadata={ | |
| "format": SPLIT_TRANSLATE_FORMAT, | |
| "config": json.dumps(asdict(self.config), sort_keys=True), | |
| "weights_sha256": tensor_checksum(state), | |
| }) | |
| def load(cls, path: str | Path, *, device="cpu", dtype=torch.float32): | |
| with safe_open(str(path), framework="pt", device="cpu") as handle: | |
| metadata = handle.metadata() | |
| if metadata.get("format") != SPLIT_TRANSLATE_FORMAT: | |
| raise ValueError("unsupported split translator file") | |
| raw = json.loads(metadata["config"]) | |
| raw["attachment_layers"] = tuple(raw["attachment_layers"]) | |
| result = cls(SplitTranslateConfig(**raw)).to(device=device, dtype=dtype) | |
| state = load_file(str(path), device=str(device)) | |
| if tensor_checksum(state) != metadata.get("weights_sha256"): | |
| raise ValueError("split translator checksum does not match") | |
| result.load_state_dict({name: value.to(dtype=dtype) for name, value in state.items()}) | |
| return result | |
| def config_checksum(config: object) -> str: | |
| return model_config_checksum(config) | |