File size: 6,038 Bytes
b8ca1f1 75e735b b8ca1f1 75e735b b8ca1f1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | ---
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.
|