Download code/graph/parameter_count.py from frontal-labs/graph-reasoner-typed-compositional: direct link, hf CLI and curl.
- Browser
- Download file 3.91 kB
-
https://huggingface.co/frontal-labs/graph-reasoner-typed-compositional/resolve/main/code/graph/parameter_count.py
- Command line
-
hf download hf://frontal-labs/graph-reasoner-typed-compositional/code/graph/parameter_count.py
-
curl -L -o parameter_count.py https://huggingface.co/frontal-labs/graph-reasoner-typed-compositional/resolve/main/code/graph/parameter_count.py
3.91 kB
| """Programmatic parameter accounting and budget validation (PLAN section 6). | |
| Section 6 asks for two things that the previous implementation did not do: a | |
| *concrete* allocation across buckets, and programmatic validation of it. Only | |
| the model total was checked before, so an allocation that put 46% of the budget | |
| in PAT (against a 15-25% target) and 1.4% in the heads (against 10-15%) passed. | |
| :func:`validate_budget` therefore checks every bucket fraction as well as the | |
| total, and reports all violations at once rather than failing on the first. | |
| """ | |
| from __future__ import annotations | |
| from collections.abc import Mapping | |
| from dataclasses import dataclass | |
| from typing import Protocol, runtime_checkable | |
| from torch import nn | |
| #: Section 6 budget ranges, as fractions of total trainable parameters. | |
| BUDGET: Mapping[str, tuple[float, float]] = { | |
| "graph_encoder": (0.10, 0.20), | |
| "pat": (0.15, 0.25), | |
| "reasoning_transformer": (0.45, 0.60), | |
| "heads": (0.10, 0.15), | |
| "embeddings": (0.05, 0.10), | |
| } | |
| class BucketedModule(Protocol): | |
| """A module that can partition its own parameters into budget buckets.""" | |
| def parameter_buckets(self) -> Mapping[str, int]: ... | |
| class ParameterReport: | |
| total: int | |
| trainable: int | |
| buckets: Mapping[str, int] | |
| def fractions(self) -> Mapping[str, float]: | |
| return { | |
| name: (count / self.total if self.total else 0.0) | |
| for name, count in self.buckets.items() | |
| } | |
| def to_dict(self) -> dict[str, object]: | |
| return { | |
| "total_parameters": self.total, | |
| "trainable_parameters": self.trainable, | |
| **dict(self.buckets), | |
| "fractions": { | |
| name: round(value, 5) for name, value in self.fractions.items() | |
| }, | |
| } | |
| def count_parameters(model: nn.Module) -> ParameterReport: | |
| total = sum(parameter.numel() for parameter in model.parameters()) | |
| trainable = sum( | |
| parameter.numel() for parameter in model.parameters() if parameter.requires_grad | |
| ) | |
| buckets = ( | |
| dict(model.parameter_buckets()) if isinstance(model, BucketedModule) else {} | |
| ) | |
| return ParameterReport(total, trainable, buckets) | |
| def validate_target( | |
| report: ParameterReport, target: int, tolerance: float = 0.20 | |
| ) -> None: | |
| """Check the trainable total lies within ``tolerance`` of ``target``.""" | |
| if target <= 0: | |
| raise ValueError("target must be positive") | |
| lower, upper = int(target * (1 - tolerance)), int(target * (1 + tolerance)) | |
| if not lower <= report.trainable <= upper: | |
| raise ValueError( | |
| f"trainable parameter count {report.trainable:,} is outside " | |
| f"[{lower:,}, {upper:,}] for target {target:,}" | |
| ) | |
| def budget_violations( | |
| report: ParameterReport, budget: Mapping[str, tuple[float, float]] | None = None | |
| ) -> tuple[str, ...]: | |
| """Return a human-readable violation per bucket outside its budget range.""" | |
| ranges = budget or BUDGET | |
| fractions = report.fractions | |
| violations: list[str] = [] | |
| for name, (low, high) in ranges.items(): | |
| value = fractions.get(name) | |
| if value is None: | |
| violations.append(f"{name}: bucket missing from parameter report") | |
| elif not low <= value <= high: | |
| violations.append( | |
| f"{name}: {value * 100:.1f}% outside target {low * 100:.0f}-{high * 100:.0f}%" | |
| ) | |
| return tuple(violations) | |
| def validate_budget( | |
| report: ParameterReport, budget: Mapping[str, tuple[float, float]] | None = None | |
| ) -> None: | |
| """Fail with every budget violation listed, not just the first.""" | |
| violations = budget_violations(report, budget) | |
| if violations: | |
| raise ValueError( | |
| "parameter allocation violates the PLAN section 6 budget:\n " | |
| + "\n ".join(violations) | |
| ) | |