Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ jobs:
run: |
mkdir -p models/$TASK
if [ "$VARIANT" = "fp32" ]; then
python -m src.cli export --task $TASK --data-root data --out-root models/$TASK
PYTHONPATH=attic python -m cli export --task $TASK --data-root data --out-root models/$TASK
else
python -m src.cli export --task $TASK --variant $VARIANT --data-root data --out-root models/$TASK
PYTHONPATH=attic python -m cli export --task $TASK --variant $VARIANT --data-root data --out-root models/$TASK
fi
- name: Compute SHA256
env:
Expand Down Expand Up @@ -94,7 +94,7 @@ jobs:
env:
TASK: ${{ needs.parse-tag.outputs.task }}
run: |
python -m src.cli evaluate --task $TASK --data-root data --out-root models \
PYTHONPATH=attic python -m cli evaluate --task $TASK --data-root data --out-root models \
> $TASK-benchmarks.json
- uses: actions/upload-artifact@v4
with:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ jobs:
with:
python-version: "3.11"
- run: pip install -e ".[dev,export]"
- name: CLI smoke test
- name: CLI smoke test (isxml)
run: |
PYTHONPATH=src python -m src.cli list
test "$(PYTHONPATH=src python -m src.cli list | jq 'length')" -ge 3
PYTHONPATH=src isxml --help
PYTHONPATH=src isxml validate --help

export-fixture:
runs-on: ubuntu-latest
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
79 changes: 79 additions & 0 deletions attic/gpu/probe_heb_parity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Probe: why did heb-diac-1.1-fp32 fail the parity gate?

Runs the exact parity harness (torch reference vs ONNX kv decode) but
PRINTS the report numbers and sample outputs instead of raising.

modal run --detach src/gpu/probe_heb_parity.py
"""

from __future__ import annotations

from pathlib import Path

import modal

REPO_ROOT = Path(__file__).resolve().parent.parent.parent

IMAGE = (
modal.Image.debian_slim(python_version="3.11")
.pip_install("torch==2.12.1", "transformers==5.14.1", "onnx==1.22.0",
"onnxruntime==1.23.2", "pyyaml>=6.0")
.add_local_dir(str(REPO_ROOT), "/root/interscript-ml", copy=True)
.workdir("/root/interscript-ml")
)

CHECKPOINT_VOLUMES = {"/volumes/rababa-checkpoints": modal.Volume.from_name("rababa-checkpoints")}
DATASET_VOLUMES = {"/datasets/rababa": modal.Volume.from_name("rababa-datasets")}
MODELS_VOLUME = modal.Volume.from_name("secryst-models")

app = modal.App("probe-heb-parity", image=IMAGE)


@app.function(
cpu=8,
memory=32 * 1024,
timeout=2 * 3600,
volumes={**CHECKPOINT_VOLUMES, **DATASET_VOLUMES, "/outputs": MODELS_VOLUME},
)
def probe() -> dict:
import json
import sys

sys.path.insert(0, "/root/interscript-ml/src")

for v in list(CHECKPOINT_VOLUMES.values()) + list(DATASET_VOLUMES.values()) + [MODELS_VOLUME]:
v.reload()

from imf.export import load_byte_seq2seq
from imf.parity import reference_decode, run_parity

ckpt = Path("/volumes/rababa-checkpoints/rababa_hebrew/run-s46-phonikud-plus/run-002-gold-ft/best")
zip_path = Path("/outputs/imf/heb-diac/heb-diac-1.1-fp32.zip")

model = load_byte_seq2seq(ckpt)

pairs = []
for line in Path("/datasets/rababa/nakdimon/test-imf.jsonl").read_text(encoding="utf-8").splitlines():
if not line.strip():
continue
row = json.loads(line)
pairs.append((row.get("input", row.get("src", "")), row.get("target", row.get("tgt", ""))))
pairs = [(s, t) for s, t in pairs if s][:40]
print(f"[probe] {len(pairs)} pairs", flush=True)

reference = reference_decode(model, [s for s, _ in pairs], max_len=128)
for i in [0, 1, 2]:
print(f"[ref {i}] {reference[i][:90]!r}", flush=True)

report = run_parity(model, zip_path, pairs, max_len=128, reference=reference)
print(f"REPORT: samples={report.samples} cer_ref={report.cer_reference}pp "
f"cer_onnx={report.cer_onnx}pp delta={report.cer_delta}pp "
f"mismatches={report.token_mismatches} passed={report.passed}", flush=True)
return {"samples": report.samples, "cer_ref": report.cer_reference,
"cer_onnx": report.cer_onnx, "delta": report.cer_delta,
"mismatches": report.token_mismatches}


@app.local_entrypoint()
def main():
print(probe.remote())
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
12 changes: 12 additions & 0 deletions cards/fas-diac-base-1.0.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
---
license: bsd-3-clause
language:
- fa
tags:
- diacritization
- byt5
- imf
- persian
metrics:
- cer
---
# fas-diac-base-1.0

Persian diacritization, base tier (run-077): byt5-small seq2seq on
Expand Down
14 changes: 14 additions & 0 deletions cards/urd-diac-base-1.0.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
---
license: other
license_name: bsd-3-clause-with-data-dependency
license_link: https://github.com/interscript/interscript-models/blob/main/cards/urd-diac-base-1.0.md
language:
- ur
tags:
- diacritization
- byt5
- imf
- urdu
metrics:
- cer
---
# urd-diac-base-1.0

Urdu diacritization, base tier (run-066): byt5-base seq2seq on the
Expand Down
15 changes: 15 additions & 0 deletions cards/urd-g2p-base-1.0.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
---
license: other
license_name: research-only-non-commercial-data
license_link: https://github.com/interscript/interscript-models/blob/main/cards/urd-g2p-base-1.0.md
language:
- ur
tags:
- g2p
- byt5
- imf
- urdu
- research-only
metrics:
- cer
---
# urd-g2p-base-1.0

Urdu grapheme-to-phoneme, base tier (run-059): byt5-base seq2seq on
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ isxml = "pipeline.cli:main"

[tool.setuptools.packages.find]
where = ["src"]
include = ["framework*", "tasks*", "imf*", "sadeedbench*", "pipeline*"]
include = ["imf*", "sadeedbench*", "pipeline*"]

[tool.pytest.ini_options]
testpaths = ["tests"]
Expand Down
2 changes: 1 addition & 1 deletion src/imf/parity.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from dataclasses import dataclass
from pathlib import Path

from framework.evaluator import char_error_rate
from imf.export import (
BYTE_OFFSET,
EOS_ID,
Expand All @@ -29,6 +28,7 @@
onnx_greedy_kv,
)
from imf.schema import ModelMetadata, Parity
from imf.text_metrics import char_error_rate


@dataclass(frozen=True)
Expand Down
34 changes: 34 additions & 0 deletions src/imf/text_metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Character-level edit metrics for the IMF parity gate.

Inlined from the retired framework module (WO56 attic move): the gate
needs CER and nothing else. Same definitions, one dependency-free
home.
"""

from __future__ import annotations

from collections.abc import Sequence


def edit_distance(a: Sequence[object], b: Sequence[object]) -> int:
"""Levenshtein distance."""
if a == b:
return 0
if not a:
return len(b)
if not b:
return len(a)
prev = list(range(len(b) + 1))
for i, ca in enumerate(a, 1):
cur = [i]
for j, cb in enumerate(b, 1):
cur.append(min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (ca != cb)))
prev = cur
return prev[-1]


def char_error_rate(pred: str, gold: str) -> float:
"""CER = edit_distance / len(gold)."""
if not gold:
return 0.0 if not pred else 1.0
return edit_distance(list(pred), list(gold)) / len(gold)
36 changes: 36 additions & 0 deletions src/pipeline/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,36 @@ def cmd_conformance(args: argparse.Namespace) -> int:
return 0 if res.passed else 1


def cmd_housekeeper(args: argparse.Namespace) -> int:
"""Live bucket inventory + prune plan. Classify-only: this verb
cannot delete anything (WO56 law) — the plan it prints is the
input to a separate, owner-authorized execution step."""
import os

from huggingface_hub import HfFileSystem

from pipeline.housekeeper import inventory, prune_plan

fs = HfFileSystem(token=os.environ.get("HF_TOKEN"))
runs = {}
for f in fs.ls(args.bucket):
name = f["name"].rsplit("/", 1)[-1]
if name.startswith(args.prefix):
runs[name] = fs.ls(f["name"])
inv = inventory(runs)
for r in inv:
status = "complete" if r.complete else "IN-FLIGHT"
notes = ("; ".join(r.notes)) if r.notes else ""
print(f"{r.name:40s} {status:10s} {r.files:4d} files "
f"{r.bytes / 1e9:6.2f} GB {notes}")
plan = prune_plan(inv)
print(f"\nprune plan: {len(plan)} candidate(s) "
"(DRY-RUN — execution is owner-authorized, this verb cannot delete):")
for p in plan:
print(" ", p)
return 0


def main(argv: list[str] | None = None) -> int:
ap = argparse.ArgumentParser(prog="isxml")
sub = ap.add_subparsers(dest="cmd", required=True)
Expand Down Expand Up @@ -130,6 +160,12 @@ def main(argv: list[str] | None = None) -> int:
c.add_argument("--zip", required=True, type=Path)
c.set_defaults(fn=cmd_conformance)

h = sub.add_parser("housekeeper",
help="bucket inventory + prune plan (dry-run only)")
h.add_argument("--bucket", default="buckets/Interscript/isx-training")
h.add_argument("--prefix", default="run-")
h.set_defaults(fn=cmd_housekeeper)

args = ap.parse_args(argv)
return args.fn(args)

Expand Down
Loading