JevLite for Apple Silicon — typed decisions on the Neural Engine
English | 日本語
Ready-to-run Core ML packages of a 149M ModernBERT typed-decision engine, built for Apple Silicon.
Hand it a state plus any set of typed questions (noul / choice / score) and it answers all of
them in one forward pass with a confidence each — fully on-device, no server, no API cost.
- 🍎 Core ML
.mlpackage, fp16, runs on the Apple Neural Engine (compute_units="cpu_and_ne") - ⚡ 13 ms per call at 256 tokens on an M2 Max — 5.4× faster than PyTorch-CPU, with the GPU left free
- 🎯 No accuracy loss vs PyTorch: 99.65% decision agreement on the full test split, accuracy −0.15 pp
- 🧩 Schema stays dynamic: questions and labels are data, not baked into the graph — new questions need no re-export
- 🔁 fp32 packages (exact parity) and the PyTorch weights included for verification and non-Apple platforms
Apple Neural Engine performance
Measured on Mac14,6 — Apple M2 Max (32 GB), macOS 26.6.2, coremltools 9.0, batch 1, median of 100 runs after 20 warmup, real encoded inputs.
| seq len | PyTorch CPU | PyTorch MPS | Core ML cpu_only |
Core ML cpu_and_ne |
|---|---|---|---|---|
| 256 | 71.5 ms | 13.4 ms | 32.8 ms | 13.2 ms |
| 512 | 123.8 ms | 22.9 ms | 64.2 ms | 37.1 ms |
| 1024 | 253.7 ms | 47.4 ms | 169.0 ms | 111.1 ms |
cpu_and_nebeats Core MLcpu_onlyby 2.5× / 1.7× / 1.5× and PyTorch-CPU by 5.4× / 3.3× / 2.3×.- It matches the GPU (MPS) at 256 tokens while keeping the GPU idle — the low-power, GPU-freeing path. At 512/1024 tokens MPS is still faster.
- ANE participation is inferred from
cpu_and_nebeing consistently faster thancpu_onlywith identical math; a per-op placement trace from Xcode was not produced.
Parity vs PyTorch (full test split, 400 cases / 2,000 decisions, cpu_and_ne)
| metric | value |
|---|---|
| decision agreement | 0.9965 |
| accuracy (Core ML / PyTorch) | 0.6445 / 0.6430 |
| confidence MAE | 0.0024 |
The fp32 packages are exact (max |Δlogit| 0.0000) but run on CPU/GPU only — the ANE is fp16-only.
Requirements
| package set | OS | hardware |
|---|---|---|
coreml/ (fp16, ANE) |
macOS 26+ | Apple Silicon |
coreml-fp32/ (exact) |
macOS 15+ | Apple Silicon |
jevlite.pt / model.safetensors |
any | CPU / CUDA / MPS |
Python: torch, transformers>=4.48, numpy, coremltools>=9.0,<10. The tokenizer is fetched from
answerdotai/ModernBERT-base.
Quick start (Mac)
hf download j-llm/jevlite --local-dir jevlite
cd jevlite
pip install torch "transformers>=4.48" numpy "coremltools>=9.0,<10"
python decide.py --backend coreml --pkg-dir coreml # interactive console on the Neural Engine
from engine import DecisionEngine
from typed_schema import Question
eng = DecisionEngine(backend="coreml", ckpt="jevlite.pt", pkg_dir="coreml",
compute_units="cpu_and_ne") # Neural Engine; backend="torch" elsewhere
print(eng.decide({"user": "I was charged twice this month"}, {
"category": Question.choice("What kind of support request is this?", {
"billing": "Charges, invoices, refunds or payment methods.",
"bug": "Something in the product is broken.",
"other": "Anything else."}),
"urgent": Question.noul("This request is urgent."),
}))
eng.close()
Run from the repo root (engine.py loads 05_serve.py by relative path). The default
coreml_mode="worker" isolates the Core ML runtime in a subprocess — use it behind any server.
Files
| path | what |
|---|---|
coreml/jevlite-{256,512,1024}.mlpackage |
Core ML fp16, Neural Engine. Smallest bucket covering the input is used. ~300 MB each. |
coreml-fp32/jevlite-{256,512,1024}.mlpackage |
Core ML fp32, exact parity reference. |
jevlite.pt |
PyTorch checkpoint (state_dict, encoder, max_len=1024, temperatures). Source of all exports. |
model.safetensors |
Same weights as jevlite.pt (fp32, 149M params); metadata holds encoder, max_len, temperatures. |
*.py, requirements.txt |
Minimal inference code from the source repo (unchanged). |
How the Core ML graph is split
The .mlpackage computes token_logits [1, S] — ModernBERT encoder + score head at every position,
inputs input_ids / attention_mask (int32). Tokenizing the questions, gathering the label-marker
positions, per-type temperature and per-question softmax run in numpy on the CPU
(coreml_backend.py, a few µs per call). That is why the schema stays fully dynamic.
Model
ModernBERT-base + Linear(d, 1) token head, trained 20 epochs on
LocalLLaMA/typed-decisions.
Test accuracy 0.6430, ECE 0.0536 (official 400-case split).
Source: eightman999/open-jev-typed-decision-engine
(fork of intikhab49/open-jev-typed-decision-engine) —
see ANE_REPORT.md and BENCHMARK.md there for full measurements and limitations.
日本語
English | 日本語
Apple Silicon 向けにそのまま動かせる、149M パラメータの ModernBERT 型付き判定エンジン(Core ML 版)です。
状態(JSON やテキスト)と、型付きの質問(noul = 真偽 / choice = 選択 / score = 段階評価)を任意の数だけ渡すと、
1回の推論ですべてに答え、それぞれの確信度を返します。端末内で完結し、サーバーも API 料金も不要です。
- 🍎 Core ML
.mlpackage(fp16)で Apple Neural Engine 上で動作(compute_units="cpu_and_ne") - ⚡ M2 Max で 256 トークンなら 1回 13 ms。PyTorch(CPU)の 5.4 倍速く、GPU は空いたまま
- 🎯 PyTorch と比べて精度は落ちない:テストデータ全件で判定一致率 99.65%、精度差 −0.15 ポイント
- 🧩 質問は自由に変えられる:質問やラベルはデータとして渡すので、グラフに焼き込まれていない。新しい質問でも再変換は不要
- 🔁 完全一致の検証用 fp32 版と、Apple 以外でも使える PyTorch の重みも同梱
Apple Neural Engine での性能
計測環境:Mac14,6 — Apple M2 Max(32 GB)、macOS 26.6.2、coremltools 9.0、バッチ 1、 ウォームアップ 20 回のあと 100 回計測した中央値、実際にエンコードした入力を使用。
| 入力長 | PyTorch CPU | PyTorch MPS | Core ML cpu_only |
Core ML cpu_and_ne |
|---|---|---|---|---|
| 256 | 71.5 ms | 13.4 ms | 32.8 ms | 13.2 ms |
| 512 | 123.8 ms | 22.9 ms | 64.2 ms | 37.1 ms |
| 1024 | 253.7 ms | 47.4 ms | 169.0 ms | 111.1 ms |
cpu_and_neは Core MLcpu_onlyより 2.5 / 1.7 / 1.5 倍、PyTorch(CPU)より 5.4 / 3.3 / 2.3 倍速い。- 256 トークンでは GPU(MPS)と同等の速さで、GPU を使わずに済む。省電力で GPU を空けておける構成。 512・1024 トークンでは MPS のほうが速い。
- Neural Engine を使っていることは、同じ計算で
cpu_and_neがcpu_onlyより常に速いことから推測している。 Xcode での演算ごとの割り当ては確認していない。
PyTorch との一致(テストデータ全件:400 件 / 2,000 判定、cpu_and_ne)
| 指標 | 値 |
|---|---|
| 判定一致率 | 0.9965 |
| 精度(Core ML / PyTorch) | 0.6445 / 0.6430 |
| 確信度の平均絶対誤差 | 0.0024 |
fp32 版は完全一致(logit 差の最大 0.0000)ですが、Neural Engine は fp16 専用のため CPU/GPU で動きます。
動作要件
| ファイル | OS | ハードウェア |
|---|---|---|
coreml/(fp16、Neural Engine) |
macOS 26 以上 | Apple Silicon |
coreml-fp32/(完全一致) |
macOS 15 以上 | Apple Silicon |
jevlite.pt / model.safetensors |
問わない | CPU / CUDA / MPS |
Python パッケージ:torch、transformers>=4.48、numpy、coremltools>=9.0,<10。
トークナイザーは answerdotai/ModernBERT-base から取得します。
使い方(Mac)
hf download j-llm/jevlite --local-dir jevlite
cd jevlite
pip install torch "transformers>=4.48" numpy "coremltools>=9.0,<10"
python decide.py --backend coreml --pkg-dir coreml # Neural Engine で対話コンソールを起動
Python から使う場合は、上の英語版にあるコード例をそのまま使えます(backend="coreml"、pkg_dir="coreml")。
engine.py は 05_serve.py を相対パスで読むので、ダウンロードしたフォルダで実行してください。
既定の coreml_mode="worker" は Core ML の実行をサブプロセスに分けるので、サーバーに組み込むときはこちらを使ってください。
ファイル
| パス | 内容 |
|---|---|
coreml/jevlite-{256,512,1024}.mlpackage |
Core ML fp16(Neural Engine 用)。入力長をカバーする最小のものが使われる。各約 300 MB。 |
coreml-fp32/jevlite-{256,512,1024}.mlpackage |
Core ML fp32。PyTorch と完全一致する検証用。 |
jevlite.pt |
PyTorch チェックポイント(state_dict、エンコーダ名、max_len=1024、温度)。すべての変換の元。 |
model.safetensors |
jevlite.pt と同じ重み(fp32、149M)。メタデータにエンコーダ名・max_len・温度を格納。 |
*.py、requirements.txt |
元リポジトリの推論コード(変更なし)。 |
Core ML 側と Python 側の分担
.mlpackage が計算するのは token_logits [1, S](ModernBERT エンコーダ+スコアヘッドを全位置で評価した値)だけです。
入力は input_ids / attention_mask(int32)。質問のトークン化、ラベル位置の取り出し、型ごとの温度、
質問ごとの softmax は CPU 上の numpy で行います(coreml_backend.py、1回数マイクロ秒)。
そのため質問の形は自由に変えられます。
モデル
ModernBERT-base に Linear(d, 1) のトークンヘッドを付け、
LocalLLaMA/typed-decisions で 20 エポック学習しました。
テスト精度 0.6430、ECE 0.0536(公式の 400 件テストセット)。
元リポジトリ:eightman999/open-jev-typed-decision-engine
(intikhab49/open-jev-typed-decision-engine のフォーク)。
詳しい計測結果と制約は、そちらの ANE_REPORT.md と BENCHMARK.md を参照してください。
- Downloads last month
- -
Model tree for j-llm/jevlite
Base model
answerdotai/ModernBERT-base