yaml-edit / src /yaml_edit /ml /model.py
erayack's picture
feat: release experimental YAML edit model under MIT
8d20d7d verified
Raw History Blame Contribute Delete
3.81 kB
"""Tiny PyTorch candidate scorer used only by the optional ML experiment."""
import math
from dataclasses import dataclass
import torch
from torch import nn
from .features import ACTION_FEATURES, FEATURE_DIMENSION
DISPOSITION_COUNT = 3
MAX_HIDDEN_DIMENSION = 128
@dataclass(frozen=True, slots=True)
class FeatureBatch:
"""Dense instruction vectors with padded, explicitly masked actions."""
vectors: torch.Tensor
actions: torch.Tensor
action_mask: torch.Tensor
def _finite_row(values: list[float], width: int, label: str) -> None:
if len(values) != width:
raise ValueError(f"A {label} has the wrong feature width.")
if any(type(value) not in (int, float) or not math.isfinite(value) for value in values):
raise ValueError(f"A {label} must contain only finite numeric features.")
def collate_features(vectors: list[list[float]], actions: list[list[list[float]]]) -> FeatureBatch:
"""Validate and pad variable action rows while retaining a boolean validity mask."""
if not vectors or len(vectors) != len(actions):
raise ValueError("Feature batches must be nonempty and aligned.")
for vector in vectors:
_finite_row(vector, FEATURE_DIMENSION, "vector")
maximum = max(1, *(len(items) for items in actions))
padded: list[list[list[float]]] = []
masks: list[list[bool]] = []
for items in actions:
for item in items:
_finite_row(item, ACTION_FEATURES, "action")
missing = maximum - len(items)
padded.append(items + [[0.0] * ACTION_FEATURES for _ in range(missing)])
masks.append([True] * len(items) + [False] * missing)
return FeatureBatch(
torch.tensor(vectors, dtype=torch.float32),
torch.tensor(padded, dtype=torch.float32),
torch.tensor(masks, dtype=torch.bool),
)
class CandidateScorer(nn.Module):
"""Encode an instruction and jointly score disposition and valid actions."""
def __init__(self, feature_dimension: int, hidden_dimension: int) -> None:
"""Create a bounded two-head scorer from explicit dimensions."""
super().__init__()
if (
type(feature_dimension) is not int
or feature_dimension != FEATURE_DIMENSION
or type(hidden_dimension) is not int
or not 1 <= hidden_dimension <= MAX_HIDDEN_DIMENSION
):
raise ValueError("Model dimensions exceed the bounded experiment contract.")
self.encoder = nn.Linear(feature_dimension, hidden_dimension)
self.disposition = nn.Linear(hidden_dimension, DISPOSITION_COUNT)
self.action = nn.Linear(ACTION_FEATURES, hidden_dimension)
def validate_architecture(self, feature_dimension: int, hidden_dimension: int) -> None:
"""Reject a scorer that does not match a validated artifact configuration."""
if (
self.encoder.in_features != feature_dimension
or self.encoder.out_features != hidden_dimension
or self.disposition.in_features != hidden_dimension
or self.action.out_features != hidden_dimension
or self.action.in_features != ACTION_FEATURES
):
raise ValueError("The model architecture does not match its configuration.")
def forward(
self, vectors: torch.Tensor, actions: torch.Tensor, action_mask: torch.Tensor
) -> tuple[torch.Tensor, torch.Tensor]:
"""Return three-way disposition logits and masked per-action logits."""
hidden = torch.tanh(self.encoder(vectors))
disposition_logits = self.disposition(hidden)
action_vectors = torch.tanh(self.action(actions))
action_logits = torch.einsum("bh,bah->ba", hidden, action_vectors)
return disposition_logits, action_logits.masked_fill(~action_mask, -1.0e9)