Gemma 3 1B Instruct β CoreML (seq=512 fixed, AnyLanguageModel-compatible)
On-device CoreML .mlpackage converted from google/gemma-3-1b-it for use with HuggingFace's AnyLanguageModel Swift framework and swift-transformers β₯ 1.0.
Input/output tensor names match the inputIds / attentionMask / logits convention required by swift-transformers 1.x LanguageModel. Drop-in compatible with CoreMLLanguageModel(url:computeUnits:chatTemplateHandler:).
Variants in this collection
grio-gemma-3-1b-coreml-anyLM-seq512(this repo) Fixed[1, 512]β short-form text enhancement / single-sentence translation. Smallest graph. Multiple safe backends.grio-gemma-3-1b-coreml-anyLM-seq1024Fixed[1, 1024]β medium-context post-processing.grio-gemma-3-1b-coreml-anyLM-seq2048RangeDim[1, 1..2048]β variable-length / long-context. Per-step cost scales with actual prompt length; fastest for typical product prompts. Must use.cpuAndGPUon the RangeDim variant.
Model details
| Spec | Value |
|---|---|
| Base | google/gemma-3-1b-it |
| Precision | Float16 (mlprogram) |
| Context | 512 tokens, fixed shape (prompt + completion combined) |
| Inputs | inputIds, attentionMask |
| Input shape | Int32 [1, 512] |
| Output | logits: Float16 [1, 512, 262144] (rank-3, per-position) |
| Min OS | iOS 18 / macOS 15 |
| Compute | .cpuAndGPU fastest; .cpuOnly and .all also work |
| Format | .mlpackage (compiled on first load) |
| Toolchain | coremltools 9.0 + torch 2.7 + transformers 5.8.1 |
Architecture note: sliding-window attention (window=512, every 6th layer is global), 4 heads / 1 KV head (extreme GQA).
Verified output (greedy, deterministic)
Actual output from this .mlpackage. Output is byte-identical to the matching seq1024 and seq2048 variants in this collection (same weights, same graph topology; only the static seq dimension differs).
- ChatML
System:
"You are a helpful assistant. Answer concisely."User:"What is the capital of France?"Output:"Paris."(stops at<end_of_turn>after 3 tokens)
See the seq2048 variant card for the full translation + post-processing verified-output table β outputs are byte-identical between variants.
Observed performance (M1 Pro, macOS)
Benchmark notes: full-pad 512.
| Compute | Predict | Load | Result |
|---|---|---|---|
.cpuAndGPU |
~1106 | ~12s | β Clean. Fastest. |
.cpuOnly |
~1106 | ~9s | β Clean output (fixed-shape variant; FP16 BNNS path is healthy at this scale). |
.all |
~1869 | ~197s | β Clean output but ANE compilation fails internally; CoreML silently falls back to GPU/CPU. Very slow load. |
.cpuAndNE |
~2883 | ~156s | β οΈ Clean output but ANE compilation slow. |
Smoke runs pad to the full 512-position graph every step, so the ms/tok number reflects fixed-shape compute over 512 positions. For typical product prompts (<200 tokens), the
seq2048RangeDim variant is faster.
iPad device perf not benchmarked in this build run.
Runtime gotchas (please read before integrating)
- All four compute units produce clean output on this fixed-shape variant. Different than the
seq2048RangeDim variant in this collection (where.cpuOnlysilently produces wrong output and.cpuAndNEerrors). Choose by perf, not correctness β.cpuAndGPUis fastest. - Fixed context window β input + output β€ 512 tokens. Pad shorter prompts with zeros and set
attentionMask = 0on padded positions. The conversion script's smoke harness does this automatically. logitsis per-position, rank-3[1, 512, 262144]. Pick the row at the last real prompt token for greedy/sampled decode.- Gemma 3 uses a two-id chat-EOS list:
<eos>(id 1) for general end-of-sequence and<end_of_turn>(id 106) for chat-turn termination.generation_config.jsonlists both. If your runtime stops only ontokenizer.eos_token_id(which returns<eos>), the model will decode past<end_of_turn>into out-of-distribution territory and produce multilingual gibberish that looks like graph corruption but isn't. Read the fulleos_token_idlist fromgeneration_config.json.swift-transformersβ₯ 1.3 handles this correctly. - The chat template lives in
chat_template.jinja(a separate file fromtokenizer_config.json).swift-transformersβ₯ 1.3 reads it correctly. Older callers that only look intokenizer_config.jsonwill not find an inline template.
Conversion notes (for the CoreML community)
Findings worth flagging for others converting Gemma 3 to CoreML:
- Use
attn_implementation="sdpa"β gives a cleaner, fewer-op MIL graph that lowers reliably. - Use
torch.export.default_decompositions()β the all-decompositions mode ({}) can SIGSEGV at 1B+ scale. - The
optimize_repeat_ops.py:433 RuntimeWarning: overflow encountered in castfires during conversion of this model. Despite the warning, output is byte-identical to PyTorch FP16 reference. Don't reflexively reconvert on seeing it β verify with a PyTorch comparison instead. - FP16 / greedy decoding is not byte-deterministic across backends. Outputs are semantically equivalent to PyTorch CPU FP16 reference but may differ on near-tied tokens. Expected behavior, not a conversion bug.
- Compute-unit safety differs per shape within this collection β see the perf table above.
.allworks onseq512, errors onseq1024. Always sweep your build before recommending a backend. chat_template.jinjamust be bundled alongside the.mlpackage. So mustgeneration_config.json,special_tokens_map.json,added_tokens.json,tokenizer.model. All included in this repo.
Usage (Swift)
import AnyLanguageModel
let modelURL: URL = // path to this .mlpackage on disk
let lm = try await CoreMLLanguageModel(
url: modelURL,
computeUnits: .cpuAndGPU,
chatTemplateHandler: { instructions, prompt in
// Gemma 3 uses its own <start_of_turn>...<end_of_turn> chat template;
// swift-transformers loads it from chat_template.jinja at runtime.
var messages: [Message] = []
if let system = instructions?.description, !system.isEmpty {
messages.append(["role": "system", "content": system])
}
messages.append(["role": "user", "content": prompt.description])
return messages
}
)
let session = LanguageModelSession(model: lm, instructions: "You are a helpful assistant.")
let response = try await session.respond(to: "Improve this text: β¦")
print(response.content)
Keep tokenizer.json, tokenizer_config.json, config.json, chat_template.jinja, generation_config.json, special_tokens_map.json, added_tokens.json, and tokenizer.model (all bundled in this repo) as siblings of the .mlpackage on disk.
Reproducibility
Conversion done with coremltools==9.0, torch==2.7.0, transformers==5.8.1. Approximate single-call recipe:
import coremltools as ct, torch, torch.nn as nn
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
"google/gemma-3-1b-it",
torch_dtype=torch.float16,
attn_implementation="sdpa",
)
model.eval()
class Wrapper(nn.Module):
def __init__(self, m): super().__init__(); self.m = m
def forward(self, inputIds, attentionMask):
return self.m(input_ids=inputIds, attention_mask=attentionMask, use_cache=False).logits
wrapper = Wrapper(model).eval()
ep = torch.export.export(
wrapper,
(torch.randint(0, 262144, (1, 128), dtype=torch.int32),
torch.ones((1, 128), dtype=torch.int32)),
).run_decompositions(torch.export.default_decompositions())
ct.convert(
ep,
inputs=[
ct.TensorType(name="inputIds", shape=(1, 512), dtype=int),
ct.TensorType(name="attentionMask", shape=(1, 512), dtype=int),
],
outputs=[ct.TensorType(name="logits")],
minimum_deployment_target=ct.target.iOS18,
compute_precision=ct.precision.FLOAT16,
convert_to="mlprogram",
)
License
Gemma Terms of Use. Weights from google/gemma-3-1b-it by Google DeepMind. Re-uploaded as a CoreML port; original model card terms apply. By using this model you agree to Google's Gemma Terms of Use and the Prohibited Use Policy.
- Downloads last month
- 3