Files
open-1b-verify/docs/open1b-local-inference.md
T

178 lines
6.7 KiB
Markdown

# Local run: open-1b on one Arc B580
- Workdir: `inference/`
- Engine: the shared llama.cpp Vulkan build, with the open1b patch
- Language: Go 1.27
## Summary
- A Go tool, `hf2gguf`, with three commands: `dump`, `convert`, `verify`.
- Validation: 39 of 39 metadata keys and 220 of 220 tensors are byte-identical.
The two files share SHA-256 `a18530417c732bb115a3501a7e1388ea7a54bd2a96c70be571d1249aa210fbae`.
- Served on one Arc (Vulkan2, PCI `07:00.0`): about 114 tok/s decode, about 892
tok/s prefill, about 3.6 GiB VRAM.
## Inputs
| Item | Path | Notes |
| --- | --- | --- |
| HF checkpoint | `models/open-1b-sft/model.safetensors` | 6.43 GB, F32 |
| Reference GGUF | `models/open-1b-sft-f16.gguf` | 3.22 GB, f16, read-only |
| HF config | `models/open-1b-sft/config.json` | see below |
| Engine | `build-vulkan/bin/llama-server` | shared prod tree, not modified |
The config: `model_type=open1b`, 24 layers, hidden 2048, heads 16 and key/value
heads 4, head_dim 128, feed-forward 5632, vocab 128256, rope_theta 500000,
rms_eps 1e-5, hybrid sliding window 512 every 5 layers, gain-free qk_norm,
embedding_norm, untied embeddings.
Devices as the engine lists them:
```
Vulkan0: AMD Ryzen 7 7700X (RADV RAPHAEL_MENDOCINO), 15835 MiB
Vulkan1: Intel Arc B580 (BMG G21), 12216 MiB
Vulkan2: Intel Arc B580 (BMG G21), 12216 MiB
Vulkan3: Intel Arc B580 (BMG G21), 12216 MiB
```
Vulkan2 maps to PCI `0000:07:00.0`, confirmed through
`/sys/class/drm/renderD129/device`.
## Tool
```
inference/hf2gguf/
main.go dispatch, manifest types
rawkv.go read-only GGUF KV walker, array support
safetensors.go safetensors header and tensor bytes
hfconfig.go config and tokenizer parsing, validation
convert.go metadata reproduction, tensor mapping, F32 to F16
verify.go byte-level comparison
convert_test.go tests
```
`go-gguf` returns an empty value for `BTypeArray`, so `rawkv.go` walks the KV
section directly to keep the tokenizer arrays (`tokens`, `merges`, `token_type`)
as raw bytes. Writing still goes through the `go-gguf` writer. GGUF
serialization is not hand-rolled.
Commands:
```bash
go run . dump -gguf models/open-1b-sft-f16.gguf -o models/open-1b-sft-f16.manifest.json
go run . convert -ref models/open-1b-sft-f16.gguf \
-safetensors models/open-1b-sft/model.safetensors \
-config models/open-1b-sft/config.json \
-tokenizer-config models/open-1b-sft/tokenizer_config.json \
-tokenizer models/open-1b-sft/tokenizer.json \
-o models/open-1b-sft-f16-go.gguf
go run . verify -ref models/open-1b-sft-f16.gguf -got models/open-1b-sft-f16-go.gguf
```
## Tensor mapping
The reference names are the contract.
| GGUF tensor | HF source | Shape | Type |
| --- | --- | --- | --- |
| `token_embd.weight` | `embedding.tok_embeddings.weight` | 2048 x 128256 | F16 |
| `token_embd_norm.weight` | `emb_norm.weight` | 2048 | F32 |
| `output.weight` | `embedding.output.weight` | 2048 x 128256 | F16 |
| `output_norm.weight` | `norm_out.weight` | 2048 | F32 |
| `blk.i.attn_q.weight` | `blocks.i.attn.wq.weight` | 2048 x 2048 | F16 |
| `blk.i.attn_k.weight` | `blocks.i.attn.wk.weight` | 2048 x 512 | F16 |
| `blk.i.attn_v.weight` | `blocks.i.attn.wv.weight` | 2048 x 512 | F16 |
| `blk.i.attn_output.weight` | `blocks.i.attn.wo.weight` | 2048 x 2048 | F16 |
| `blk.i.ffn_gate.weight` | `blocks.i.ffn.w_gate_up.weight`, first half | 2048 x 5632 | F16 |
| `blk.i.ffn_up.weight` | `blocks.i.ffn.w_gate_up.weight`, second half | 2048 x 5632 | F16 |
| `blk.i.ffn_down.weight` | `blocks.i.ffn.w_down.weight` | 5632 x 2048 | F16 |
| `blk.i.attn_norm.weight` | `blocks.i.norm1.weight` | 2048 | F32 |
| `blk.i.ffn_norm.weight` | `blocks.i.norm2.weight` | 2048 | F32 |
The fused `w_gate_up` is split at the row boundary. Per-channel `*.weight_scale`
tensors in the checkpoint are dropped; the reference has none. F32 to F16 uses
round-to-nearest, ties to even, matching the numpy and torch casts.
## Validation
```
metadata: 39 checked, 0 mismatches
tensors: 220 checked, 220 byte-identical, 0 mismatches
verification PASSED
```
```
$ cmp models/open-1b-sft-f16.gguf models/open-1b-sft-f16-go.gguf && echo IDENTICAL
IDENTICAL
```
Both files hash to
`a18530417c732bb115a3501a7e1388ea7a54bd2a96c70be571d1249aa210fbae`.
Notable metadata, all byte-identical: `general.architecture=open1b`,
`open1b.block_count=24`, `open1b.context_length=4096`,
`open1b.attention.head_count=16`, `open1b.attention.head_count_kv=4`,
`open1b.rope.freq_base=500000`, `open1b.attention.sliding_window=512`,
`open1b.attention.sliding_window_pattern=5`, `tokenizer.ggml.model=gpt2`,
`tokenizer.ggml.pre=gpt-2`, `tokenizer.ggml.eos_token_id=4`. The full list is in
the manifest JSON.
`go build`, `go vet`, and `go test -race` are clean.
## Serve
```bash
setsid --fork ./build-vulkan/bin/llama-server \
-m models/open-1b-sft-f16-go.gguf \
--device Vulkan2 -ngl 99 -c 32768 -fa off --jinja \
--spec-type ngram-map-k,ngram-cache \
-t 16 --parallel 1 --alias open-1b \
--host 127.0.0.1 --port 8085 >/tmp/open1b.log 2>&1 </dev/null &
```
| Metric | Value |
| --- | --- |
| Decode | 111.7 to 117.2 tok/s |
| Prefill | about 892 tok/s |
| VRAM | about 3.6 GiB, released after stop |
Sample outputs, greedy:
```
prompt: The three primary colors are
out: red, blue, and yellow.
prompt: Q: What is the capital of France? A:
out: Paris
```
Stopped with `kill` after reading the pid from `ss -ltnp`.
## Caveats
1. Chat template. With the vendor tail `...assistant<|end_header|>` followed by a
blank line, this checkpoint emits an end-of-turn token first and returns an
empty reply. Reproduced with and without the speculative decoder, so it is not
the decoder. A single newline, an extra newline, or a seed token produces a
coherent answer. Since our GGUF is byte-identical to the reference, this is a
property of the released checkpoint.
2. Context cap. `open1b.context_length` is 4096, so the requested 32768 is capped.
3. The load warning `special_eos_id is not in special_eog_ids` also appears for
the reference file, so it is not introduced by the conversion.
4. Long greedy generation without a repetition penalty loops on this small model.
5. Process note. During one early check the response was piped through `python3`
to pretty-print JSON. That was a mistake and a breach of the no-Python rule.
The tool itself is entirely Go, and nothing in the pipeline depends on Python.
No Python files or virtualenv were created for the tool.
## Files
| Path | What |
| --- | --- |
| `hf2gguf/` | Go module with tests |
| `hf2gguf/README.md` | usage |
| `models/open-1b-sft-f16-go.gguf` | our output |
| `models/open-1b-sft-f16.manifest.json` | dumped contract |
| `models/open-1b-sft-f16.gguf` | reference |
| `/tmp/open1b.log` | server log |