| --- |
| license: gemma |
| library_name: cellm |
| pipeline_tag: text-generation |
| tags: |
| - function-calling |
| - tool-use |
| - gemma3 |
| - quantization |
| - on-device |
| - cellm |
| --- |
| |
| # functiongemma-270m β cellm builds |
|
|
| Function-calling Gemma3 (270M) converted to the `.cellm` format for the |
| [cellm](https://github.com/jeffasante/cellm) CPU runtime. |
|
|
| | File | Size | Recipe | |
| |---|---|---| |
| | `functiongemma-270m-f16.cellm` | 511 MB | unquantized reference | |
| | `functiongemma-270m-int8e.cellm` | 257 MB | int8 weights, int8 embedding | |
| | `functiongemma-270m-int8-e4g32.cellm` | 186 MB | int8 weights, int4 group-32 embedding | |
| | `tokenizer.json` | 32 MB | β | |
|
|
| Architecture: `gemma3_text`, 18 layers, hidden 640, 4 query heads / 1 KV head, |
| head_dim 256, vocab 262144, context 32768. |
| |
| ## Which one to use |
| |
| **`int8e` (257 MB) unless you need the smaller file.** It matches the f16 |
| baseline on 11 of 16 prompts; `int8-e4g32` matches on 9. The 71 MB saving costs |
| two prompts and buys no speed. |
| |
| Use `int8-e4g32` (186 MB) only when the memory budget is binding. |
| |
| ## Benchmark |
| |
| 16 prompts, greedy decoding (`--temperature 0`), 64 max new tokens, |
| `--stop-tokens 1,50,106`. Apple Silicon, CPU backend. Two separate measures: |
| |
| - **vs HF ref** β exact generated-token-ID match against the original |
| HuggingFace `transformers` model. Measures end-to-end correctness. |
| - **vs f16** β exact match against our own f16 `.cellm` build. Isolates |
| quantization damage from pre-existing model behaviour. |
| |
| | Build | Size | vs HF ref | vs f16 | Avg prefill | | |
| |---|---|---|---|---|---| |
| | f16 | 511 MB | 12/16 | 16/16 | 4.19 s | | |
| | int8 | 416 MB | 11/16 | 12/16 | 2.95 s | not published | |
| | **int8e** | **257 MB** | **11/16** | **11/16** | **2.90 s** | recommended | |
| | int8-e4g32 | 186 MB | 9/16 | 9/16 | 2.94 s | | |
| |
| `int8` is measured but not shipped: `int8e` matches it on every metric that |
| matters β same 11/16, same speed β in 159 MB less. |
| |
| Prefill is ~400 prompt tokens (the tool declarations dominate). Decode ran |
| 12β32 tokens at roughly 26 tok/s. |
| |
| Two honest caveats: |
| |
| **The f16 baseline is only 12/16.** Four failures are inherited from the base |
| model, not caused by quantization. Do not read "11/16" as "5 quantization |
| regressions". |
| |
| **Quantization below int8 buys no speed.** 257 MB and 186 MB are within noise |
| of each other (2.90 s vs 2.94 s). The prefill path skips the `lm_head` matmul |
| at every position except the last, so the embedding table β the only thing |
| that differs between these two builds β is barely touched. Shrinking it saves |
| memory, not time. |
|
|
| ## Sample outputs (`int8-e4g32`) |
|
|
| Correct: |
|
|
| ``` |
| Turn on wifi |
| <start_function_call>call:set_wifi{enabled:true}<end_function_call> |
| |
| What is the weather in Accra? |
| <start_function_call>call:get_weather{city:<escape>Accra<escape>,unit:<escape>c<escape>}<end_function_call> |
| |
| Turn off wifi and check the weather in London in fahrenheit |
| <start_function_call>call:set_wifi{enabled:false}<end_function_call> |
| <start_function_call>call:get_weather{city:<escape>London<escape>,unit:<escape>f<escape>}<end_function_call> |
| |
| Who was the first president of Ghana? |
| I apologize, but I cannot assist with historical inquiries about past |
| leaders of countries. My current capabilities are focused on ... |
| ``` |
|
|
| Dropped calls β these are the two regressions unique to `int8-e4g32`; both |
| emit only one of the two required calls: |
|
|
| ``` |
| Turn on wifi and set brightness to 50 |
| f16: call:set_wifi{enabled:true} + call:set_brightness{level:50} |
| int8-e4g32: call:set_wifi{enabled:true} <- brightness lost |
| |
| Make the screen brighter, set it to 90 |
| f16: call:set_brightness{level:90} + call:set_brightness{level:40} |
| int8-e4g32: call:set_brightness{level:40} <- first call lost |
| ``` |
|
|
| Failures shared by every build, including f16 β base-model behaviour: |
|
|
| ``` |
| Wake me up at 6:30 tomorrow |
| call:set_alarm{time:<escape>14:30<escape>} <- wrong time, all builds |
| |
| Text Ama that I'm running late |
| call:send_message{...,recipient:<escape>person@example.com<escape>} |
| <- invents a recipient |
| ``` |
|
|
| ## Usage |
|
|
| ```bash |
| infer \ |
| --model functiongemma-270m-int8e.cellm \ |
| --tokenizer tokenizer.json \ |
| --prompt "$PROMPT" \ |
| --gen 64 --temperature 0 \ |
| --stop-tokens 1,50,106 |
| ``` |
|
|
| `--stop-tokens 1,50,106` is required. The converter writes `eos_token_id: 106` |
| but the model actually stops on 50; without the explicit list, generation runs |
| past the end of the function call. |
|
|
| The prompt must follow the Gemma chat template, with tool declarations wrapped |
| in `<start_function_declaration>` / `<end_function_declaration>` before the |
| `<start_of_turn>user` turn. |
|
|
| ## Quantization notes |
|
|
| These builds came out of a search for a sub-100 MB model. That target was not |
| reached, and the reasons are worth recording: |
|
|
| **The embedding tolerates 4 bits; the linear weights do not.** With f16 |
| weights, an int4 group-32 embedding scores 5/5 on the smoke set. With f16 |
| embeddings, int4 group-32 *weights* score 0/5 β fluent but wrong text such as |
| *"I am sorry, but I cannot assist with this request."* Hence the asymmetric |
| recipe: int8 everywhere except the embedding. |
|
|
| **int2 fails regardless of group size.** The fixed codebook |
| `{-1.5, -0.5, 0.5, 1.5}` holds relative reconstruction error at 0.43 on the |
| embedding even at group 32 (0.471 per-row β 0.431 at g32). Grouping cannot fix |
| a codebook limit. Getting under 100 MB requires the 168M-parameter embedding at |
| 2 bits, so the target is out of reach without a learned codebook. |
|
|
| The int4 scale is near-optimal, so that 10% weight error is inherent rather |
| than a tuning artifact: `amax/7` gives 0.1008 on `mlp.up_proj` at g32 versus |
| 0.0967 for an exhaustively searched scale. |
|
|
| **CPU only.** Grouped scales are implemented in the CPU path. The Metal i4 |
| path still passes `hidden` as the group size, so grouped models will produce |
| wrong results on GPU. `int8e` is unaffected by this and works on both. |
|
|
| ## License |
|
|
| Gemma Terms of Use, inherited from the base model. |
|
|