| --- |
| license: gemma |
| base_model: google/gemma-4-12B-it-qat-q4_0-unquantized |
| tags: |
| - core-ai |
| - coreai |
| - apple |
| - gemma |
| - gemma-4 |
| - on-device |
| - metal |
| pipeline_tag: text-generation |
| library_name: coreai |
| --- |
| |
| # Gemma 4 12B (dense) β Core AI |
|
|
| Apple **Core AI** (`.aimodel`) conversion of Google's **Gemma 4 12B** dense text decoder, |
| ported directly from the QAT release |
| [`google/gemma-4-12B-it-qat-q4_0-unquantized`](https://huggingface.co/google/gemma-4-12B-it-qat-q4_0-unquantized). |
| Decode-only, runs on the **stock pipelined engine** on Apple Silicon (M-series Macs). |
|
|
| > **First Core AI runtime for a β₯16-head Γ head_dim-512 full-attention model.** Gemma 4 12B's |
| > full (global) attention layers have a 16-head Γ 512 Q tensor (16 KB fp16) that overflows |
| > MPSGraph's GPU decode scratch heap β the stock SDPA crashes at the first token |
| > ([apple/coreai-models#27](https://github.com/apple/coreai-models/issues/27)). These bundles |
| > ship a **custom Metal flash-decode kernel** on the full layers that removes the offending op, |
| > so the model runs. (The plain non-kernel bundles still crash β these `_msdpa` bundles are the |
| > runnable ones.) |
| |
| <!-- gen-cards:use-it begin id=gemma-4-12b (managed by scripts/gen-cards β edit cards.json / QuickStart.swift, not this block) --> |
| ## Use it |
| |
| βΆοΈ **Run it (source)** β the [ChatDemo runner](https://github.com/john-rocky/coreai-kit/tree/main/Examples/ChatDemo) |
| (GUI + CLI, one app for every chat model in the catalog): |
|
|
| ```bash |
| git clone https://github.com/john-rocky/coreai-kit |
| open coreai-kit/Examples/ChatDemo/ChatDemo.xcodeproj |
| # β Run, then pick "Gemma 4 12B" in the model picker |
| |
| # agents / headless (macOS): |
| cd coreai-kit/Examples/ChatDemo |
| swift run chat-cli --model gemma-4-12b --prompt "What can you do, offline?" |
| ``` |
|
|
| π» **Build with it** β complete; the glue is kit API, copy-paste runs: |
|
|
| ```swift |
| import CoreAIKit |
| |
| let chat = try await ChatSession(catalog: "gemma-4-12b") |
| let reply = try await chat.respond(to: prompt) |
| // reply: the answer, generated fully on-device |
| ``` |
|
|
| The take-home is [`Examples/ChatDemo/Sources/QuickStart.swift`](https://github.com/john-rocky/coreai-kit/blob/main/Examples/ChatDemo/Sources/QuickStart.swift) |
| β this exact code as one typed function, no UI; the CLI is an argument shell over it, and |
| the GUI drives the same `ChatSession` across turns for its transcript. |
| Multi-turn? Hold the `ChatSession` and call `respond(to:)` per turn β it keeps the |
| conversation history; `streamResponse(to:)` yields tokens as they decode. |
|
|
| **Integration checklist** |
|
|
| - SPM: `https://github.com/john-rocky/coreai-kit` β product **CoreAIKit** |
| - Info.plist: none needed |
| - Entitlements: none needed (macOS) |
| - First run downloads the model β 13.0 GB (Mac) β then it loads from the |
| local cache (Application Support; progress via the `downloadProgress` callback) |
| - Measure in Release β Debug is ~3Γ slower on per-token host work |
| <!-- gen-cards:use-it end --> |
|
|
| ## Bundles (`gpu-pipelined/`) |
|
|
| | bundle | quant | size | decode (M4 Max) | quality | |
| |---|---|---|---|---| |
| | `gemma4_12b_qat_decode_int8lin_msdpa_g8` | int8 (per-block-32) | 14 GB | **~23 tok/s** (prefill 27.5) | verified-clean: engine greedy == fp32 oracle | |
| | `gemma4_12b_qat_decode_int4linsym_msdpa_g8` | int4 (q4_0-aligned absmax) | 8.2 GB | **~33 tok/s** (prefill 43.4) | answers correctly, slightly 4-bit-lossy phrasing | |
| |
| The `_g8` suffix is the higher-occupancy flash-decode kernel (8 SIMD-groups per head split the |
| global layers' KV scan) β it holds throughput at long context (int8 decode 17.5 β **20.3 tok/s** at |
| 1024 generated tokens vs the simple kernel) with identical numerics. |
|
|
| **int8 is the verified-clean default** (its teacher-forced greedy reproduces the fp32 oracle's |
| "The capital of France is Paris." exactly). **int4** is the faster / smaller option (16 GB-Mac |
| accessible) at a small quality cost β the same precision class as MLX 4-bit, *not* a conversion |
| bug (the int8 graph is exact). |
|
|
| ## Architecture |
|
|
| Clean dense `gemma4_unified` text decoder β **no** PLE / AltUp / Laurel / MoE / KV-sharing |
| (unlike the on-device E2B/E4B siblings). 48 layers, hidden 3840, 16 heads, vocab 262144, final |
| logit softcap 30, tied embeddings. 5:1 sliding:full interleave; dual head_dim (sliding 256 / full |
| `global_head_dim` 512); full layers use a single global KV head with `attention_k_eq_v` (value = |
| raw k_proj). Both attention shapes ride **one** growing KV pair, so the bundle loads on the stock |
| `CoreAIPipelinedEngine` (2 states, no engine patch); the full layers' SDPA runs as a custom Metal |
| flash-decode kernel. |
| |
| ## Usage |
| |
| Download a bundle and run with Apple's `llm-runner` / `llm-benchmark` (the pipelined engine; set |
| `COREAI_CHUNK_THRESHOLD=1`): |
| |
| ```bash |
| huggingface-cli download mlboydaisuke/Gemma-4-12B-CoreAI \ |
| --include "gpu-pipelined/gemma4_12b_qat_decode_int8lin_msdpa_g8/*" \ |
| --local-dir ./gemma4-12b-coreai |
| |
| COREAI_CHUNK_THRESHOLD=1 llm-runner \ |
| --model ./gemma4-12b-coreai/gpu-pipelined/gemma4_12b_qat_decode_int8lin_msdpa_g8 \ |
| --prompt "What is the capital of France?" --max-tokens 64 --chunk-size 1 |
| ``` |
| |
| Each bundle is self-contained: the `.aimodel`, `metadata.json`, and the Gemma tokenizer. |
| |
| ## Conversion |
| |
| Community zoo (recipe, overlays, model card): |
| [github.com/john-rocky/coreai-model-zoo β `zoo/gemma4-12b.md`](https://github.com/john-rocky/coreai-model-zoo/blob/main/zoo/gemma4-12b.md). |
| |
| ## License |
| |
| Gemma β governed by the [Gemma Terms of Use](https://ai.google.dev/gemma/terms). By using these |
| weights you agree to those terms. The conversion (Core AI bundles, custom Metal kernel) adds no |
| additional restrictions. |
| |