def records(*, rows: int, seed: int) -> Iterator[dict]:
rng = np.random.default_rng(seed)
for _ in range(rows):
a, b = map(float, rng.uniform(-1, 1, size=2))
yield {"a": a, "b": b, "u": a + 2 * b, "v": 2 * a - b}
def prediction(model: rf.Model, rows: list[dict], *, corrupt: bool = False) -> dict:
inputs = [{"a": row["a"], "b": row["b"]} for row in rows]
if corrupt:
for row in inputs:
row.update({name: 1000.0 for name in TARGETS})
output = model.predict(inputs)["predictions"].to_pylist()
return {
name: np.asarray([row[f"record/{name}"]["content"] for row in output], dtype=np.float64) for name in TARGETS
}
def scores(rows: list[dict], predicted: dict, means: dict) -> dict:
measured = {}
for name, values in predicted.items():
actual = np.asarray([row[name] for row in rows])
baseline = float(np.sqrt(np.mean((actual - means[name]) ** 2)))
rmse = float(np.sqrt(np.mean((actual - values) ** 2)))
measured[name] = {"rmse": rmse, "baseline_rmse": baseline, "nrmse": rmse / baseline}
return measured
def equal(first, second) -> bool:
"""Compare tensor and extension-owned state, including normalization."""
if isinstance(first, torch.Tensor):
return isinstance(second, torch.Tensor) and torch.equal(first, second)
if isinstance(first, dict):
return (
isinstance(second, dict)
and first.keys() == second.keys()
and all(equal(value, second[name]) for name, value in first.items())
)
if isinstance(first, (tuple, list)):
return (
type(first) is type(second)
and len(first) == len(second)
and all(equal(a, b) for a, b in zip(first, second, strict=True))
)
return type(first) is type(second) and first == second
class Curve(lit.Callback):
"""Observe fixed validation checkpoints without restarting the optimizer."""
def __init__(self, rows: list[dict], means: dict, budget: int):
self.rows, self.means = rows, means
self.checkpoints = {32, 128, budget}
self.measurements = []
def on_train_start(self, trainer, pl_module):
self.measurements.append(
{
"step": 0,
"scores": scores(self.rows, prediction(pl_module, self.rows), self.means),
}
)
def on_train_batch_end(self, trainer, pl_module, outputs, batch, batch_idx):
if trainer.global_step in self.checkpoints:
self.measurements.append(
{
"step": trainer.global_step,
"scores": scores(self.rows, prediction(pl_module, self.rows), self.means),
}
)
def fit(model: rf.Model, *, seed: int, split: int, budget: int, accelerator: str) -> dict:
lit.seed_everything(seed, workers=True)
training = list(records(rows=2048, seed=split))
means = {name: float(np.mean([row[name] for row in training])) for name in TARGETS}
curve = Curve(list(records(rows=512, seed=split + 1)), means, budget)
model.optimizer = rf.adamw(learning_rate=3e-3, fused=False)
data = rf.SyntheticDataModule(
model=model,
train=partial(records, rows=2048, seed=split),
validate=partial(records, rows=512, seed=split + 1),
seed=seed,
)
trainer = lit.Trainer(
accelerator=accelerator,
devices=1,
max_epochs=-1,
max_steps=budget,
callbacks=[curve],
logger=False,
enable_progress_bar=False,
enable_model_summary=False,
enable_checkpointing=False,
deterministic=True,
num_sanity_val_steps=0,
)
trainer.fit(model, datamodule=data)
model.eval()
optimized = {id(p) for group in trainer.optimizers[0].param_groups for p in group["params"]}
current = {id(p) for p in model.parameters() if p.requires_grad}
return {
"steps": trainer.global_step,
"optimizer_covers_current_parameters": current == optimized,
"validation_curve": curve.measurements,
}