Instructions to use Meanblock/JEV-CPU with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Meanblock/JEV-CPU with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("zero-shot-classification", model="Meanblock/JEV-CPU")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Meanblock/JEV-CPU", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download benchmarks/mlx_benchmark.py from Meanblock/JEV-CPU: direct link, hf CLI and curl.
- Browser
- Download file 15.9 kB
-
https://huggingface.co/Meanblock/JEV-CPU/resolve/main/benchmarks/mlx_benchmark.py
- Command line
-
hf download hf://Meanblock/JEV-CPU/benchmarks/mlx_benchmark.py
-
curl -L -o mlx_benchmark.py https://huggingface.co/Meanblock/JEV-CPU/resolve/main/benchmarks/mlx_benchmark.py
15.9 kB
| """Create-only Apple Silicon evidence: quality, cache reuse, and compact generation. | |
| Run from the repository root. Reuses the frozen fixtures and evaluator; does not | |
| modify the CUDA evidence. Each run requires a new output directory. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| from collections import defaultdict | |
| import hashlib | |
| import json | |
| from pathlib import Path | |
| import platform | |
| import statistics | |
| import time | |
| import evaluate | |
| from mlx_evidence import read_bytes | |
| from decision_vs_generation import compact_messages | |
| from semif_phase1 import mlx_backend as backend | |
| from semif_phase1.direct import encode_prompt | |
| def read(path): | |
| return [json.loads(line) for line in read_bytes(path).decode("utf-8").splitlines() if line.strip()] | |
| def write(path, data): | |
| with Path(path).open("x") as stream: | |
| json.dump(data, stream, indent=2, allow_nan=False) | |
| stream.write("\n") | |
| def write_rows(path, rows): | |
| with Path(path).open("x") as stream: | |
| for row in rows: | |
| stream.write(json.dumps(row, allow_nan=False) + "\n") | |
| def choice(row): | |
| return row["option_ids"][max(range(len(row["probabilities"])), key=row["probabilities"].__getitem__)] | |
| def compare(reference, candidates): | |
| old = evaluate.indexed(reference, "reference") | |
| new = evaluate.indexed(candidates, "candidate") | |
| if old.keys() != new.keys(): | |
| raise ValueError("Comparison requires identical decision IDs") | |
| differences, flips, prompt_mismatches = [], [], [] | |
| for row in candidates: | |
| base = old[row["id"]] | |
| left = dict(zip(base["option_ids"], base["probabilities"])) | |
| right = dict(zip(row["option_ids"], row["probabilities"])) | |
| if left.keys() != right.keys(): | |
| raise ValueError("Option IDs differ") | |
| differences.append(max(abs(left[key] - right[key]) for key in left)) | |
| if choice(row) != choice(base): | |
| flips.append({"id": row["id"], "from": choice(base), "to": choice(row), | |
| "reference_probabilities": left, "candidate_probabilities": right}) | |
| if row["prompt_sha256"] != base["prompt_sha256"] or row["input_tokens"] != base["input_tokens"]: | |
| prompt_mismatches.append(row["id"]) | |
| return {"rows": len(candidates), "argmax_flips": flips, "prompt_mismatches": prompt_mismatches, | |
| "max_probability_difference": max(differences), | |
| "mean_max_probability_difference": statistics.mean(differences)} | |
| def quality(model, tokenizer, metadata, output, reference_run=None): | |
| import mlx.core as mx | |
| datasets = {} | |
| report = {} | |
| for name in ("authored144", "perturbations108"): | |
| gold = read(f"benchmarks/data/{name}.jsonl") | |
| backend.score(model, tokenizer, gold[0], metadata) | |
| mx.reset_peak_memory() | |
| started = time.perf_counter() | |
| predictions = [] | |
| for index, row in enumerate(gold): | |
| predictions.append(backend.score(model, tokenizer, row, metadata)) | |
| if (index + 1) % 36 == 0: | |
| print(f"{name}: {index + 1}/{len(gold)}", flush=True) | |
| elapsed = time.perf_counter() - started | |
| write_rows(output / f"{name}.jsonl", predictions) | |
| baseline = read(f"results/raw/predictions/direct-{name}.jsonl") | |
| report[name] = {"evaluation": evaluate.evaluate(gold, predictions, comparison=baseline), | |
| "vs_published_torch": compare(baseline, predictions), | |
| "wall_seconds": elapsed, "peak_mlx_bytes": mx.get_peak_memory()} | |
| if reference_run is not None: | |
| reference = read(reference_run / f"{name}.jsonl") | |
| report[name]["vs_mlx_reference"] = compare(reference, predictions) | |
| datasets[name] = (gold, predictions) | |
| gold, base = datasets["authored144"] | |
| perturb_gold, perturb = datasets["perturbations108"] | |
| by_id = {row["id"]: row for row in base} | |
| variants = defaultdict(list) | |
| for row, prediction in zip(perturb_gold, perturb): | |
| variants[row["provenance"]["variant"]].append((row, prediction)) | |
| report["perturbation_stability"] = {} | |
| for name, pairs in variants.items(): | |
| references = [dict(by_id[row["provenance"]["base_id"]], id=row["id"]) for row, _ in pairs] | |
| comparison = compare(references, [pred for _, pred in pairs]) | |
| # Wording/order/context intentionally change the prompts in this comparison. | |
| comparison.pop("prompt_mismatches") | |
| report["perturbation_stability"][name] = comparison | |
| missing = [row for row in gold if row["provenance"]["variant"] == "missing"] | |
| report["missing_evidence_confident_non_insufficient_at_0_8"] = sum( | |
| choice(by_id[row["id"]]) != "insufficient" and max(by_id[row["id"]]["probabilities"]) >= .8 | |
| for row in missing | |
| ) | |
| write(output / "quality.json", report) | |
| return {name: report[name]["evaluation"]["mean_family_balanced_accuracy"] for name in datasets} | |
| def shape(model, tokenizer, metadata, output): | |
| import mlx.core as mx | |
| rows = read("benchmarks/data/shape777.jsonl") | |
| groups = defaultdict(list) | |
| for row in rows: | |
| groups[row["group_id"]].append(row) | |
| if len(rows) != 777 or len(groups) != 37 or any(len(group) != 21 for group in groups.values()): | |
| raise ValueError("Expected 37 groups of 21 decisions") | |
| first = next(iter(groups.values())) | |
| predictions, reports = {}, [] | |
| for mode in ("direct", "serial", "shared"): | |
| def run(group): | |
| if mode == "direct": | |
| return [backend.score(model, tokenizer, row, metadata) for row in group] | |
| if mode == "serial": | |
| scorer = backend.SerialPrefixScorer(model, tokenizer, metadata) | |
| return [scorer.score(row) for row in group] | |
| return backend.score_shared(model, tokenizer, group, metadata)[0] | |
| run(first) | |
| mx.clear_cache() | |
| mx.reset_peak_memory() | |
| values, durations = [], [] | |
| with (output / f"shape777-{mode}.jsonl").open("x") as stream: | |
| for index, group in enumerate(groups.values()): | |
| mark = time.perf_counter() | |
| scored = run(group) | |
| durations.append(time.perf_counter() - mark) | |
| values.extend(scored) | |
| for row in scored: | |
| stream.write(json.dumps(row, allow_nan=False) + "\n") | |
| stream.flush() | |
| if (index + 1) % 5 == 0: | |
| print(f"shape/{mode}: {index + 1}/37 states; active={mx.get_active_memory() / 2**30:.2f} GiB; cached={mx.get_cache_memory() / 2**30:.2f} GiB", flush=True) | |
| elapsed = sum(durations) | |
| predictions[mode] = values | |
| reports.append({"mode": mode, "wall_seconds": elapsed, "decisions_per_second": len(values) / elapsed, | |
| "state_p50_seconds": statistics.median(durations), "state_seconds": durations, | |
| "peak_mlx_bytes": mx.get_peak_memory()}) | |
| print(json.dumps(reports[-1], default=str), flush=True) | |
| result = {"runs": reports, "vs_fresh": {mode: compare(predictions["direct"], predictions[mode]) | |
| for mode in ("serial", "shared")}} | |
| write(output / "shape777.json", result) | |
| return reports | |
| def generate(model, tokenizer, rows, max_tokens=128): | |
| import mlx.core as mx | |
| from mlx_lm.generate import generate_step | |
| mx.synchronize() | |
| started = time.perf_counter() | |
| prompt = tokenizer.apply_chat_template(compact_messages(rows[0]["state"], rows), tokenize=False, | |
| add_generation_prompt=True, enable_thinking=False) | |
| ids = tokenizer.encode(prompt, add_special_tokens=False) | |
| tokens, events = [], [] | |
| generator = generate_step(mx.array(ids), model, max_tokens=max_tokens) | |
| try: | |
| for token, _ in generator: | |
| tokens.append(token) | |
| events.append({"token_id": token, "seconds": time.perf_counter() - started}) | |
| if token in tokenizer.eos_token_ids: | |
| break | |
| finally: | |
| generator.close() | |
| mx.synchronize() | |
| text = tokenizer.decode(tokens, skip_special_tokens=True) | |
| parsed = None | |
| try: | |
| candidate = json.loads(text) | |
| if isinstance(candidate, list) and len(candidate) == len(rows) and all( | |
| isinstance(value, str) and value in {"yes", "no"} for value in candidate | |
| ): | |
| parsed = candidate | |
| except json.JSONDecodeError: | |
| pass | |
| return {"total_seconds": time.perf_counter() - started, "input_tokens": len(ids), | |
| "output_tokens": len(tokens), "output_text": text, "choices": parsed, | |
| "valid_complete_array": parsed is not None, "timeline": events, | |
| "time_to_first_token_seconds": events[0]["seconds"] if events else None, | |
| "prompt_sha256": hashlib.sha256(prompt.encode()).hexdigest()} | |
| def generation(model, tokenizer, metadata, output): | |
| import mlx.core as mx | |
| rows = read("benchmarks/data/shape777.jsonl")[:21] | |
| backend.score_shared(model, tokenizer, rows, metadata) | |
| generate(model, tokenizer, rows[:1], 16) | |
| direct_runs, generation_runs = [], [] | |
| for _ in range(3): | |
| mx.reset_peak_memory() | |
| values, timing = backend.score_shared(model, tokenizer, rows, metadata) | |
| direct_runs.append({**timing, "peak_mlx_bytes": mx.get_peak_memory(), "outputs": values}) | |
| mx.reset_peak_memory() | |
| generated = generate(model, tokenizer, rows) | |
| generated["peak_mlx_bytes"] = mx.get_peak_memory() | |
| generated["agreement_with_direct"] = (sum(choice(row) == value for row, value in zip(values, generated["choices"])) / len(rows) | |
| if generated["choices"] is not None else None) | |
| generation_runs.append(generated) | |
| direct_median = statistics.median(run["total_seconds"] for run in direct_runs) | |
| generation_median = statistics.median(run["total_seconds"] for run in generation_runs) | |
| result = {"direct_runs": direct_runs, "generation_runs": generation_runs, | |
| "direct_median_seconds": direct_median, "generation_median_seconds": generation_median, | |
| "generation_over_direct_ratio": generation_median / direct_median, | |
| "all_generated_arrays_valid": all(run["valid_complete_array"] for run in generation_runs)} | |
| write(output / "generation.json", result) | |
| return {key: value for key, value in result.items() if not key.endswith("runs")} | |
| def diagnostic(model, tokenizer, metadata, output): | |
| """Freeze a small pilot before running full quality/systems populations.""" | |
| import mlx.core as mx | |
| from transformers import AutoTokenizer | |
| original_tokenizer = AutoTokenizer.from_pretrained(metadata["source"], revision=metadata["revision"], trust_remote_code=False) | |
| groups = [read("benchmarks/data/shape777.jsonl")[:4]] | |
| sample = read("examples/decisions.jsonl")[0] | |
| groups.append([dict(sample, id=f"pilot-{n}", question=sample["question"] + " " * n) for n in range(3)]) | |
| report = [] | |
| for rows in groups: | |
| for row in rows: | |
| if encode_prompt(tokenizer, row, 4096) != encode_prompt(original_tokenizer, row, 4096): | |
| raise AssertionError("MLX and original tokenizers differ") | |
| fresh = [backend.score(model, tokenizer, row, metadata) for row in rows] | |
| serial = backend.SerialPrefixScorer(model, tokenizer, metadata) | |
| cached = [serial.score(row) for row in rows] | |
| shared, _ = backend.score_shared(model, tokenizer, rows, metadata) | |
| reversed_shared, _ = backend.score_shared(model, tokenizer, rows[::-1], metadata) | |
| report.append({"fresh": fresh, "serial": cached, "shared": shared, | |
| "serial_vs_fresh": compare(fresh, cached), "shared_vs_fresh": compare(fresh, shared), | |
| "reordered_vs_shared": compare(shared, reversed_shared)}) | |
| mx.synchronize() | |
| write(output / "diagnostic.json", report) | |
| return [{key: value for key, value in part.items() if "_vs_" in key} for part in report] | |
| def main(): | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--model", default="Qwen/Qwen3.5-4B") | |
| parser.add_argument("--revision", default="851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a") | |
| parser.add_argument("--bits", type=int, choices=(4, 8)) | |
| parser.add_argument("--mlx-cache-limit-mib", type=int, default=backend.DEFAULT_CACHE_LIMIT_MIB, | |
| help="Inactive allocation cache in MiB (default: 256; 0 disables caching)") | |
| parser.add_argument("--suite", choices=("diagnostic", "quality", "shape", "generation", "quantization", "all"), default="all") | |
| parser.add_argument("--reference-run", type=Path, help="Completed unquantized MLX quality run for quantization comparison") | |
| parser.add_argument("--output", type=Path, required=True) | |
| args = parser.parse_args() | |
| if args.mlx_cache_limit_mib < 0: | |
| parser.error("--mlx-cache-limit-mib must be nonnegative") | |
| if args.suite == "quantization" and args.bits is None: | |
| parser.error("The quantization suite requires --bits 4 or --bits 8") | |
| reference_metadata = None | |
| if args.suite == "quantization" and args.reference_run is None: | |
| parser.error("The quantization suite requires --reference-run with unquantized MLX evidence") | |
| if args.reference_run is not None: | |
| reference_model = json.loads((args.reference_run / "manifest.json").read_text())["model"] | |
| if reference_model["quantization"] or reference_model["source"] != args.model or reference_model["revision"] != args.revision: | |
| parser.error("Reference must use the same unquantized source model and revision") | |
| reference_metadata = {"path": str(args.reference_run), "sha256": { | |
| name: hashlib.sha256(read_bytes(args.reference_run / name)).hexdigest() | |
| for name in ("manifest.json", "authored144.jsonl", "perturbations108.jsonl")}} | |
| args.output.mkdir(parents=True, exist_ok=False) | |
| model, tokenizer, metadata = backend.load_model( | |
| args.model, args.revision, args.bits, cache_limit_mib=args.mlx_cache_limit_mib) | |
| import mlx.core as mx | |
| manifest = {"model": metadata, "hardware": mx.device_info(), "macos": platform.mac_ver()[0], | |
| "python": platform.python_version(), "suite": args.suite, "reference_run": reference_metadata, | |
| "source_sha256": {str(path): hashlib.sha256(path.read_bytes()).hexdigest() for path in [ | |
| Path("benchmarks/mlx_benchmark.py"), Path("benchmarks/mlx_evidence.py"), | |
| Path("benchmarks/evaluate.py"), | |
| Path("benchmarks/decision_vs_generation.py"), | |
| *sorted(Path("src/semif_phase1").glob("*.py")), | |
| ]}, | |
| "timing_scope": "Warm GPU execution including prompt preparation and CPU readout; excludes model loading and result writes. MLX operations are evaluated and synchronized.", | |
| "fixture_sha256": {path.name: hashlib.sha256(path.read_bytes()).hexdigest() | |
| for path in Path("benchmarks/data").glob("*.jsonl")}} | |
| write(args.output / "manifest.json", manifest) | |
| summaries = {} | |
| suites = {"all": ("diagnostic", "quality", "shape", "generation"), | |
| "quantization": ("diagnostic", "quality", "generation")}.get(args.suite, (args.suite,)) | |
| for name, run in (("diagnostic", diagnostic), ("quality", quality), ("shape", shape), ("generation", generation)): | |
| if name in suites: | |
| summaries[name] = (run(model, tokenizer, metadata, args.output, args.reference_run) if name == "quality" | |
| else run(model, tokenizer, metadata, args.output)) | |
| print(json.dumps({name: summaries[name]}), flush=True) | |
| write(args.output / "summary.json", summaries) | |
| if __name__ == "__main__": | |
| main() | |