Gemma-4-12B-PsiLM / MANIFEST.md
ryoji-info's picture
MANIFEST.md: task switch
5839b8c verified
|
Raw
History Blame
13.1 kB
# MANIFEST — Hugging Face repo `ryoji-info/Gemma-4-12B-PsiLM`
Everything in this directory (`release/gemma-4-12b-psilm/`) is uploaded as-is to the
model repo; nothing outside it is needed. The backbone (`mlx-community/gemma-4-12B-it-4bit`)
is **not** uploaded — the script downloads it at first run.
## Files to upload (and where each one came from)
| path in the HF repo | source in the GitHub checkout | size | sha256 |
|---|---|---:|---|
| `README.md` | written for this release (the HF model card, YAML front matter) | 17 KB | — |
| `psilm_infer.py` | written for this release | 42 KB | — |
| `requirements.txt` | written for this release | 1 KB | — |
| `MANIFEST.md` | this file (harmless to upload; drop it if you prefer) | — | — |
| `psilm-banner.png` | `assets/psilm-banner.png` (1600 px wide) | 1.35 MB | `4cd65f32fe4aab5b49b66681a841b0128628e8d94c75494ae84f941b49c48089` |
| `bridges/gemma-4-12b-4bit-mlx-1d-value-selective/bridges.safetensors` | `results/hf_export/bridges/gemma-4-12b-4bit-mlx-1d-value-selective/bridges.safetensors` (= `results/stage2_gemma12b/bridges.npz`, step 7000, learned-pointer tensors dropped) | 102,068,660 B | `f6ef8946c41b3cfa17df7c22bcab2c5cffbd856c2dd608970a364cc14d5e8f7d` |
| `bridges/gemma-4-12b-4bit-mlx-1d-value-selective/config.json` | `results/hf_export/bridges/gemma-4-12b-4bit-mlx-1d-value-selective/config.json` | 1,287 B | `8a5add382beb65a0214f5e8b0b640e5d5df6f19e9a6caf1945400d2fe1e6bba5` |
| `physics/fno_burgers_singlemode.safetensors` | `results/hf_export/physics/fno_burgers_singlemode.safetensors` (= `results/stage2/fno.pt`; loaders verified identical, max weight and field difference 0.0) | 552,076 B | `7bb0076c85cdcf2505a9079c05964e3eb77ac4a776eccf34216953f3c37bfcdd` |
The copies were made with `cp` on 2026-09-06 and the hashes match the sources
(`shasum -a 256`). Not uploaded: `__pycache__/`, `.DS_Store`.
### To add later (the two runs in progress)
`psilm_infer.py` has the `--task {1d,multimode,2d}` switch (2026-09-06; the 1D path is
unchanged: on the 0.5B the previous script and this one print the same numbers and text,
`--question-only` byte-for-byte, full runs differing only in the timings). When the Gemma
multi-mode and 2D runs finish, export them and add:
| path in the HF repo | source in the GitHub checkout | size | sha256 |
|---|---|---:|---|
| `bridges/gemma-4-12b-4bit-mlx-multimode/bridges.safetensors` | the stage-2b Gemma checkpoint `results/stage2b_gemma12b_2b/bridges.npz` (see the export recipe below) | — | — |
| `bridges/gemma-4-12b-4bit-mlx-multimode/config.json` | written from `results/stage2b_gemma12b_2b/bridges.npz.meta` in the schema below | — | — |
| `physics/fno_burgers_multimode.safetensors` | `results/hf_export/physics/fno_burgers_multimode.safetensors` (= `results/stage2b/fno.pt`; loads with `load_fno_safetensors`, verified against the torch FNO1d to 4e-7 in the smoke below) | 552,076 B | `8cebd7a82d0f5da13ae1cfb74848d74d43b98b98cd09767655f6c4ac069f879b` |
| `bridges/gemma-4-12b-4bit-mlx-2d-dpot/bridges.safetensors` | the stage-2d Gemma checkpoint `results/stage2d_gemma12b_2d/bridges.npz` (tag as in `eval/mlx_stage2d_train.py`'s docstring) | — | — |
| `bridges/gemma-4-12b-4bit-mlx-2d-dpot/config.json` | written from its `bridges.npz.meta` in the schema below | — | — |
| `physics/dpot_tiny_fisher2d_finetuned.safetensors` | `results/hf_export/physics/dpot_tiny_fisher2d_finetuned.safetensors` (= `results/stage2d/dpot_ft.pt`, bare DPOT-Tiny state-dict keys; verified identical to the .pt in the smoke below) | 30,144,524 B | `88472c199fed489c05ac5ceaf9113c607dd28d95e45385f62e52fac6494b15d8` |
| `physics/model_Ti.pth` | `results/stage2d/model_Ti.pth` -- the upstream DPOT-Tiny base checkpoint (hzk17/DPOT, Apache-2.0). **Ship it, or rely on the download:** `psilm_infer.py --task 2d` looks for it at `--dpot-base` (default `physics/model_Ti.pth`) and, when absent, downloads `model_Ti.pth` from the Hugging Face repo `hzk17/DPOT` (the repo `psilm/physics/dpot_wrapper.py` names; the file is listed there, download verified 2026-09-06) into the HF cache. `DPOTPhysics` loads the base first and the fine-tuned safetensors then replaces every one of its 67 tensors (`strict=True`), so the base only satisfies the wrapper's constructor. | 90,475,962 B | `074c337f9b3a3c70253f8022ce6be7e7dfb809a91a7b00e46fbfedf9611d767f` |
Then fill the two *in progress* rows of `README.md` ("Bridges in this repository") from their
`final_eval.json`, and add `einops>=0.8` to `requirements.txt` (the vendored DPOT definition
imports it; only the 2D task needs it).
**The 2D task needs a clone.** `psilm/physics/dpot_wrapper.py` imports `dpot_model` from
`<repo>/vendor/`, which the pip package (`[tool.setuptools.packages.find] include = ["psilm*"]`)
does not ship. `psilm_infer.py` adds `$PSILM_REPO/vendor` to `sys.path` when `PSILM_REPO`
points at a clone (even with the package installed) and exits with a message naming this
otherwise; the README's usage section says so. Packaging `vendor/dpot_model.py` inside `psilm`
would lift the requirement.
#### `config.json` schema (multimode and 2d)
`psilm_infer.py` requires every key below for `--task multimode` / `--task 2d` and exits naming
the missing one (e.g. `config.json: missing key(s) ['coupling.l_rev', 'physics']`). The 1D
directory keeps its existing, more lenient config (defaults inferred from the tensors). `task`
is also what the script auto-detects when `--bridges` is given without `--task` (falling back to
the directory name: `multimode`, `2d`/`dpot`).
```json
{
"task": "multimode", // "multimode" | "2d" (required)
"backbone": "mlx-community/gemma-4-12B-it-4bit", // informational
"hf_tokenizer": "mlx-community/gemma-4-12B-it-4bit", // used for the chat template (else --backbone)
"bridges_class": "psilm.mlx.multimode.make_bridges_multi (PsiBridgesMLX, n_params=6)",
// 2d: "psilm.mlx.bridges2d.PsiBridges2DMLX"
"construct": { // required, all five keys; passed verbatim to the
"d_model": 3840, // constructor the trainer used:
"channel": "value", // make_bridges_multi(**construct) (eval/mlx_stage2b_train.py)
"inj_cap": 0.2, // PsiBridges2DMLX(**construct) (eval/mlx_stage2d_train.py)
"gate_bias": 0.0, // = meta["args"]{channel, inj_cap, gate_bias, readout_norm}
"readout_norm": "dim"
},
"coupling": {"l_fwd": 20, "l_rev": 30, "n_layers": 48}, // required; meta["l_fwd"], meta["l_rev"]; n_layers is checked
"physics": { // required; "file" is relative to the repo root and is the
"file": "physics/fno_burgers_multimode.safetensors", // default for --physics
"dpot_base": "physics/model_Ti.pth" // 2d only, informational (the CLI flag --dpot-base decides)
},
"training": {"...": "free-form record, as in the 1D config"},
"license": "apache-2.0"
}
```
(JSON has no comments; the file itself carries none.) Values must match how the checkpoint was
trained: the script constructs the bridges from `construct`, then checks every tensor shape in
`bridges.safetensors` against the module and refuses on a mismatch or an unknown tensor; missing
tensors are allowed (`strict=False`) and reported as unused (the multimode export may drop the
retired learned pointer `fwd.x0_query`, `fwd.x0_key.*`, as the 1D export did; the 2D bridges
have no such tensors). `fwd.dim_mu` / `fwd.dim_sigma` (the `readout_norm: "dim"` calibration)
are ordinary tensors of the checkpoint and must be included.
#### Export recipe (maintainer, from the checkout root)
```python
import json, mlx.core as mx
from pathlib import Path
task, src, dst = "multimode", Path("results/stage2b_gemma12b_2b"), Path("release/gemma-4-12b-psilm/bridges/gemma-4-12b-4bit-mlx-multimode")
# task, src, dst = "2d", Path("results/stage2d_gemma12b_2d"), Path("release/gemma-4-12b-psilm/bridges/gemma-4-12b-4bit-mlx-2d-dpot")
meta = json.loads((src / "bridges.npz.meta").read_text()); a = meta["args"]
w = {k: v for k, v in mx.load(str(src / "bridges.npz")).items()
if not k.startswith(("fwd.x0_query", "fwd.x0_key."))} # unused learned pointer (multimode only)
dst.mkdir(parents=True, exist_ok=True)
mx.save_safetensors(str(dst / "bridges.safetensors"), w)
cfg = {"task": task, "backbone": meta["model"], "hf_tokenizer": a["hf_tokenizer"],
"bridges_class": {"multimode": "psilm.mlx.multimode.make_bridges_multi (PsiBridgesMLX, n_params=6)",
"2d": "psilm.mlx.bridges2d.PsiBridges2DMLX"}[task],
"construct": {"d_model": 3840, "channel": a["channel"], "inj_cap": a["inj_cap"],
"gate_bias": a["gate_bias"], "readout_norm": a["readout_norm"]},
"coupling": {"l_fwd": meta["l_fwd"], "l_rev": meta["l_rev"], "n_layers": 48},
"physics": {"multimode": {"file": "physics/fno_burgers_multimode.safetensors"},
"2d": {"file": "physics/dpot_tiny_fisher2d_finetuned.safetensors", "dpot_base": "physics/model_Ti.pth"}}[task],
"training": {"step": meta["step"], "args": a}, "license": "apache-2.0"}
(dst / "config.json").write_text(json.dumps(cfg, indent=1))
```
Then `python psilm_infer.py --task multimode` / `--task 2d` from `release/gemma-4-12b-psilm`
must print the three sections with a `PsiLM match` verdict on the defaults before upload.
#### Smoke record (2026-09-06, mechanics only)
Run with randomly initialised bridges of the right shapes for `mlx-community/Qwen2.5-0.5B-Instruct-4bit`
(d_model 896, 24 layers, coupling 10/15), built with the trainers' own constructors
(`make_bridges_multi(896, gate_bias=0.0, inj_cap=0.2, channel="value", readout_norm="dim")`,
`PsiBridges2DMLX(d_model=896, ...)` with the same arguments), saved as `bridges.safetensors` +
`config.json` in the schema above, with the physics files from `results/hf_export/physics/`:
- `--task multimode` (auto-detected from the directory name in one run), single-mode default and
the two-mode combination, with and without the baseline arm: three sections printed; the
`[3] physics model (FNO)` value agreed with an independent torch computation
(`psilm.stage2.bridges.build_ic_multi` + `psilm.physics.fno.FNO1d` loaded from
`results/stage2b/fno.pt` + `fourier_interp`) to 5e-8 (single) and 4e-7 (combo).
- `--task 2d` on MPS with `--dpot-base results/stage2d/model_Ti.pth`, and on CPU with the base
absent (downloaded from `hzk17/DPOT`): three sections printed; `[3] physics model (DPOT)`
agreed with the torch stage-2d path (`DPOTPhysics` from `model_Ti.pth` + `results/stage2d/dpot_ft.pt`,
`psilm.stage2d.bridges2d.build_ic_2d`, `features_and_field`, `fisher2d.bilinear_periodic`, CPU)
to 7e-7 (MPS) and 0.0 (CPU).
- Error paths: a config missing `coupling.l_rev` and `physics` exits naming both keys; `--task 2d`
on a multimode directory exits on the `task` mismatch; malformed `--modes` exits.
- The coupled answers were garbage, as expected from random bridges (the backbone's own reply
template, no `equals`), which the script reports as "reply left the trained template".
## Upload (maintainer runs this)
From the GitHub checkout root, with `HF_TOKEN` set (or `huggingface-cli login` done):
```bash
cd /Users/rxiii/Documents/GitHub/PsiLM
HF_HUB_DISABLE_XET=1 .venv/bin/python - <<'EOF'
from huggingface_hub import HfApi
api = HfApi()
repo = "ryoji-info/Gemma-4-12B-PsiLM"
api.create_repo(repo, repo_type="model", private=True, exist_ok=True)
api.upload_folder(
folder_path="release/gemma-4-12b-psilm",
repo_id=repo,
repo_type="model",
commit_message="Gemma-4-12B-PsiLM: bridges, Burgers FNO, one-command CLI, model card",
ignore_patterns=["__pycache__/*", "*.pyc", ".DS_Store"],
)
print("UPLOAD DONE")
EOF
```
`HF_HUB_DISABLE_XET=1` keeps the upload on the classic LFS path (the Xet backend has
stalled on this machine's earlier uploads). Make the repo public afterwards with
`api.update_repo_settings(repo, private=False)` or from the repo's settings page, together
with `ryoji-info/PsiLM-bridges` and `ryoji-info/PsiLM-physics`, which the card links to.
## After upload: the one-command check
```bash
huggingface-cli download ryoji-info/Gemma-4-12B-PsiLM --local-dir /tmp/g4psilm && cd /tmp/g4psilm
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python psilm_infer.py
```
Note for the `pip install` line: the `psilm @ git+...` requirement needs the GitHub
`pyproject.toml` to use package discovery (`[tool.setuptools.packages.find] include = ["psilm*"]`,
changed on 2026-09-06 in the working tree from `packages = ["psilm"]`, which shipped only the
top-level package without `psilm.mlx`, `psilm.stage2`, `psilm.physics`). That change must be
pushed before the requirement installs a usable package; until then the script says so, and the
`PSILM_REPO=/path/to/clone` fallback works regardless.