Ling-3.0-tiny-GGUF / REPRODUCE.md
Mike0021's picture
Publish v1.0 model card, provenance, and validation
31596cd verified
|
Raw
History Blame Contribute Delete
16.3 kB
# Reproducing this GGUF release
The release is pinned to immutable source, converter, calibration, and
validation revisions. BailingMoE3 support is not yet merged in upstream
`llama.cpp`; use the exact revision below.
## 1. Build the converter and runtime
```bash
git clone https://github.com/aetherbird/llama.cpp.git
git -C llama.cpp checkout d8d862521e9ad842f2b47f3b392b039317782aa0
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -r llama.cpp/requirements.txt
python -m pip install --extra-index-url https://download.pytorch.org/whl/cu128 \
gguf==0.19.0 huggingface_hub==0.36.2 numpy==1.26.4 \
protobuf==4.25.9 safetensors==0.8.0 sentencepiece==0.2.2 \
tokenizers==0.22.2 'torch==2.8.0+cu128' tqdm==4.70.0 \
transformers==4.57.6
cmake -S llama.cpp -B llama.cpp/build \
-DCMAKE_BUILD_TYPE=Release \
-DGGML_CUDA=ON \
-DGGML_NATIVE=OFF \
-DCMAKE_CUDA_ARCHITECTURES=native
cmake --build llama.cpp/build --config Release --parallel \
--target llama-cli llama-completion llama-server llama-tokenize llama-imatrix \
llama-quantize llama-perplexity
```
For a CPU-only conversion build, omit `-DGGML_CUDA=ON` and
`-DCMAKE_CUDA_ARCHITECTURES=native`.
## 2. Download and convert the immutable source revision
```bash
hf download inclusionAI/Ling-3.0-tiny \
--revision a2ee06c0f2de5b171701aee7f73f70a1da75483b \
--local-dir Ling-3.0-tiny
(cd Ling-3.0-tiny && sha256sum -c ../source-safetensors.sha256)
python llama.cpp/convert_hf_to_gguf.py Ling-3.0-tiny \
--outtype bf16 \
--outfile Ling-3.0-tiny-BF16.gguf \
--verbose
```
The expected BF16 result is 526 tensors, 15,803,475,232 bytes, with SHA-256
`2020d58d44887c4078c310dad0363b2af0898a9ed6cc46675c0d23201833952e`.
## 3. Collect the importance matrix
```bash
python -m pip install duckdb==1.3.2
hf download lemon07r/bartowski-imatrix-v5-semantic \
bartowski-imatrix-v5-semantic.txt \
--repo-type dataset \
--revision a306f203ee4323e0afe846ae02c2daafe17384d9 \
--local-dir calibration
hf download eaddario/imatrix-calibration \
combined_all_micro.parquet \
--repo-type dataset \
--revision e87ed55dcba9d9c3a3e41539f3e728e981b1daa4 \
--local-dir calibration/eaddario
python - <<'PY'
import duckdb
source = "calibration/eaddario/combined_all_micro.parquet"
output = "calibration/eaddario/combined_all_micro.txt"
rows = duckdb.connect().execute(
"SELECT content FROM read_parquet(?)", [source]
).fetchall()
with open(output, "w", encoding="utf-8", newline="\n") as handle:
for (content,) in rows:
if content:
handle.write(content.replace("\x00", ""))
handle.write("\n")
PY
printf '%s\n' \
'ff879b5a748f822ef539e43c596a3f44ab922f0295ee209d4220d9f86e86a063 calibration/bartowski-imatrix-v5-semantic.txt' \
'94389921e1f67b180a99de28c3090b41ce6f1960eb13abad21b7eba7cbe11b26 calibration/eaddario/combined_all_micro.parquet' \
'fdb2d41abf04a2fb207502741a561a5a9ab385eb0c44a450eae676c410955946 calibration/eaddario/combined_all_micro.txt' \
| sha256sum -c -
llama.cpp/build/bin/llama-imatrix \
--model Ling-3.0-tiny-BF16.gguf \
--file calibration/bartowski-imatrix-v5-semantic.txt \
--output-file Ling-3.0-tiny-imatrix-primary.gguf \
--output-format gguf \
--gpu-layers all --ctx-size 4096 --batch-size 4096 --ubatch-size 512 \
--threads 16 --threads-batch 16 --device CUDA0 \
--split-mode none --main-gpu 0 --fit off --flash-attn off \
--offline --no-ppl \
--output-frequency 25
llama.cpp/build/bin/llama-imatrix \
--model Ling-3.0-tiny-BF16.gguf \
--file calibration/eaddario/combined_all_micro.txt \
--in-file Ling-3.0-tiny-imatrix-primary.gguf \
--output-file Ling-3.0-tiny-imatrix.gguf \
--output-format gguf \
--gpu-layers all --ctx-size 4096 --batch-size 4096 --ubatch-size 512 \
--threads 16 --threads-batch 16 --device CUDA0 \
--split-mode none --main-gpu 0 --fit off --flash-attn off \
--offline --no-ppl \
--output-frequency 25
```
The primary text SHA-256 is
`ff879b5a748f822ef539e43c596a3f44ab922f0295ee209d4220d9f86e86a063`.
The supplement parquet and extracted text SHA-256 values are respectively
`94389921e1f67b180a99de28c3090b41ce6f1960eb13abad21b7eba7cbe11b26`
and `fdb2d41abf04a2fb207502741a561a5a9ab385eb0c44a450eae676c410955946`.
They are used only for activation statistics, not training or evaluation.
`--process-output` is intentionally omitted. The pinned tool documentation
states that it is typically better not to use the importance matrix for
`output.weight`, which is why collection for that tensor defaults to false.
The commands above are tensor-equivalent reproduction commands. The released
matrix and importance-aware model files also embed path strings. Byte-for-byte
SHA-256 reproduction requires the primary dataset at
`/workspace/ling3/calibration/bartowski-imatrix-v5-semantic.txt`, the extracted
supplement at `/workspace/ling3/calibration/eaddario/combined_all_micro.txt`,
and the final matrix at `/tmp/Ling-3.0-tiny-imatrix.gguf`. The source directory
basename must be `Ling-3.0-tiny`. Different path spellings change metadata
bytes without changing the collected statistics or quantized tensor values.
The released final matrix is 44,016,768 bytes with SHA-256
`e8b15d131f9ce294f922c5c387f7a69829c12100d6a35bb1635a2b859083c3f0`.
It contains 332 entries from 162 complete chunks, and all 8,832 routed-expert
count slots are nonzero.
## 4. Quantize directly from BF16
The matrix is deliberately used for K-quants as well as IQ quants. No output
is requantized from Q8 or another reduced-precision artifact.
```bash
llama.cpp/build/bin/llama-quantize \
Ling-3.0-tiny-BF16.gguf Ling-3.0-tiny-Q8_0.gguf Q8_0 32
for quant in Q6_K Q5_K_M Q4_K_M Q4_K_S IQ4_XS Q3_K_M IQ3_M IQ2_M; do
llama.cpp/build/bin/llama-quantize \
--imatrix Ling-3.0-tiny-imatrix.gguf \
Ling-3.0-tiny-BF16.gguf "Ling-3.0-tiny-${quant}.gguf" "$quant" 32
done
```
Do not add `--allow-requantize` or `--pure` when reproducing these files.
IQ4_NL and MXFP4_MOE candidates were measured but intentionally rejected; they
are not part of the published artifact set.
## 5. Held-out PPL and KLD comparison
Validation uses WikiText-2 from `ggml-org/ci` at revision
`927b3642933080f1b0e811e2f916e14c292992f9`. The extracted test file SHA-256
is `173c87a53759e0201f33e0ccf978e510c2042d7f2cb78229d9a50d79b9e7dd08`.
```bash
hf download ggml-org/ci wikitext-2-raw-v1.zip \
--repo-type dataset \
--revision 927b3642933080f1b0e811e2f916e14c292992f9 \
--local-dir validation
unzip -q validation/wikitext-2-raw-v1.zip -d validation
printf '%s\n' \
'ef7edb566e3e2b2d31b29c1fdb0c89a4cc683597484c3dc2517919c615435a11 validation/wikitext-2-raw-v1.zip' \
'173c87a53759e0201f33e0ccf978e510c2042d7f2cb78229d9a50d79b9e7dd08 validation/wikitext-2-raw/wiki.test.raw' \
| sha256sum -c -
```
The archive SHA-256 is
`ef7edb566e3e2b2d31b29c1fdb0c89a4cc683597484c3dc2517919c615435a11`.
First create the BF16 log-probability reference. In this pinned build,
`--kl-divergence-base` is an alias for `--save-all-logits`.
```bash
llama.cpp/build/bin/llama-perplexity \
--model Ling-3.0-tiny-BF16.gguf \
--file validation/wikitext-2-raw/wiki.test.raw \
--gpu-layers all --ctx-size 512 --batch-size 512 --ubatch-size 512 \
--chunks 32 --threads 16 --threads-batch 16 --device CUDA0 \
--split-mode none --main-gpu 0 --fit off --flash-attn off \
--kv-offload --op-offload --no-repack \
--cache-type-k f16 --cache-type-v f16 \
--no-warmup --offline \
--kl-divergence-base validation/bf16-c512-chunks32.kld
```
Then compare each quant using the same context and batch sizes. The comparator
reads the exact tokens and chunk count from the reference file.
```bash
llama.cpp/build/bin/llama-perplexity \
--model Ling-3.0-tiny-BF16.gguf \
--gpu-layers all --ctx-size 512 --batch-size 512 --ubatch-size 512 \
--threads 16 --threads-batch 16 --device CUDA0 \
--split-mode none --main-gpu 0 --fit off --flash-attn off \
--kv-offload --op-offload --no-repack \
--cache-type-k f16 --cache-type-v f16 \
--no-warmup --offline \
--kl-divergence-base validation/bf16-c512-chunks32.kld \
--kl-divergence \
> validation/kld-BF16.log 2>&1
for quant in Q8_0 Q6_K Q5_K_M Q4_K_M Q4_K_S IQ4_XS Q3_K_M IQ3_M IQ2_M; do
llama.cpp/build/bin/llama-perplexity \
--model "Ling-3.0-tiny-${quant}.gguf" \
--gpu-layers all --ctx-size 512 --batch-size 512 --ubatch-size 512 \
--threads 16 --threads-batch 16 --device CUDA0 \
--split-mode none --main-gpu 0 --fit off --flash-attn off \
--kv-offload --op-offload --no-repack \
--cache-type-k f16 --cache-type-v f16 \
--no-warmup --offline \
--kl-divergence-base validation/bf16-c512-chunks32.kld \
--kl-divergence \
> "validation/kld-${quant}.log" 2>&1
done
python validation/parse_kld.py --output validation/kld-results.json \
BF16=validation/kld-BF16.log \
Q8_0=validation/kld-Q8_0.log \
Q6_K=validation/kld-Q6_K.log \
Q5_K_M=validation/kld-Q5_K_M.log \
Q4_K_M=validation/kld-Q4_K_M.log \
Q4_K_S=validation/kld-Q4_K_S.log \
IQ4_XS=validation/kld-IQ4_XS.log \
Q3_K_M=validation/kld-Q3_K_M.log \
IQ3_M=validation/kld-IQ3_M.log \
IQ2_M=validation/kld-IQ2_M.log
```
Do not combine this KLD workflow with `--ppl-stride`; it uses a different
evaluation path. Comparison logs must be newly truncated, never appended. The
parser requires a complete terminal statistics block because error paths in
this pinned `llama-perplexity` can still return process status zero.
The stored BF16 reference is 2,565,373,716 bytes with SHA-256
`afa4dc9bd2d995dd85d8614494dc9cccec8e0e4bd6b2c24efc81d3bba96638f7`.
## 6. Structure, tokenizer, and deterministic generation
Generate a canonical shape map from BF16, then compare every quant against it.
The validator also checks the architecture metadata, Q-LoRA tensors, exact
embedded chat template, NEXTN/MTP absence, matrix provenance, and the exact
filename-bound tensor-type inventory of every published model. Its separate
MXFP4 whitelist is retained for auditing rejected candidates.
```bash
python validation/check_gguf_structure.py \
--gguf-dump .venv/bin/gguf-dump \
--model Ling-3.0-tiny-BF16.gguf \
--source-template Ling-3.0-tiny/chat_template.jinja \
--write-reference-shapes validation/bf16-tensor-shapes.json \
--output validation/structure-BF16.json
for quant in Q8_0 Q6_K Q5_K_M Q4_K_M Q4_K_S IQ4_XS Q3_K_M IQ3_M IQ2_M; do
python validation/check_gguf_structure.py \
--gguf-dump .venv/bin/gguf-dump \
--model "Ling-3.0-tiny-${quant}.gguf" \
--source-template Ling-3.0-tiny/chat_template.jinja \
--reference-shapes validation/bf16-tensor-shapes.json \
--output "validation/structure-${quant}.json"
done
```
Generate the independent Transformers reference only after verifying all 32
source shards and the pinned control files:
```bash
python3 -m venv .hfref-venv
.hfref-venv/bin/python -m pip install \
--extra-index-url https://download.pytorch.org/whl/cu128 \
accelerate==1.10.1 einops==0.8.1 fla-core==0.5.1 \
huggingface_hub==0.36.2 numpy==2.1.2 safetensors==0.8.0 \
tokenizers==0.22.2 'torch==2.8.0+cu128' tqdm==4.70.0 \
transformers==4.57.6
.hfref-venv/bin/python validation/hf_reference.py \
Ling-3.0-tiny validation/hf-reference.json \
--source-weight-manifest source-safetensors.sha256
python validation/compare_tokenizer.py \
--llama-tokenize llama.cpp/build/bin/llama-tokenize \
--model Ling-3.0-tiny-BF16.gguf \
--hf-reference validation/hf-reference.json \
--output validation/tokenizer-comparison.json
```
The controlled raw-generation smoke uses the exact same full prompt in both
runtimes. `compare_generation.py` now fails if the supplied prompt differs
from the reference, even when the generated suffix happens to match.
```bash
prompt='The capital of France is Paris. The capital of Germany is Berlin. The capital of Japan is'
for quant in BF16 Q8_0 Q6_K Q5_K_M Q4_K_M Q4_K_S IQ4_XS Q3_K_M IQ3_M IQ2_M; do
llama.cpp/build/bin/llama-completion \
--model "Ling-3.0-tiny-${quant}.gguf" \
--prompt "$prompt" --predict 12 \
--ctx-size 512 --batch-size 512 --ubatch-size 128 \
--threads 16 --threads-batch 16 --device CUDA0 --gpu-layers all \
--split-mode none --main-gpu 0 --fit off --flash-attn off \
--kv-offload --op-offload --no-repack \
--cache-type-k f16 --cache-type-v f16 \
--seed 1 --samplers temperature --temperature 0 \
-no-cnv --no-context-shift --no-warmup --no-display-prompt \
--color off --offline --log-colors off --no-log-timestamps \
--check-tensors \
> "validation/gguf-greedy-${quant}.txt" \
2> "validation/load-${quant}.log"
python validation/compare_generation.py \
--artifact "$quant" \
--hf-reference validation/hf-reference.json \
--gguf-output "validation/gguf-greedy-${quant}.txt" \
--case greedy_capitals_12 --prompt-text "$prompt" \
--mismatch-policy fail \
--output "validation/generation-${quant}.json"
done
```
## 7. Multiple-choice collapse and 32K execution screens
```bash
hf download ikawrakow/validation-datasets-for-llama.cpp \
mmlu-validation.bin --repo-type dataset \
--revision 37884b81b4957f1950a53b6ff48d77c8dd5e430c \
--local-dir validation
printf '%s\n' \
'470af3a74eccacfaf6f43b08aabf510f61e6c92fe20d17241ded934151e225fa validation/mmlu-validation.bin' \
| sha256sum -c -
for quant in BF16 Q5_K_M Q4_K_M Q4_K_S IQ4_XS Q3_K_M IQ3_M IQ2_M; do
llama.cpp/build/bin/llama-perplexity \
--model "Ling-3.0-tiny-${quant}.gguf" \
--file validation/mmlu-validation.bin \
--multiple-choice --multiple-choice-tasks 500 --seed 1 \
--gpu-layers all --ctx-size 512 --batch-size 512 --ubatch-size 512 \
--threads 16 --threads-batch 16 --device CUDA0 \
--split-mode none --main-gpu 0 --fit off --flash-attn off \
--kv-offload --op-offload --no-repack \
--cache-type-k f16 --cache-type-v f16 \
--no-warmup --offline --no-log-timestamps \
> "validation/mmlu-${quant}.log" 2>&1
done
```
The pinned tool prints “TruthfulQA” internally, but the supplied binary is the
pinned MMLU validation file. Results are summarized in
`validation/multiple-choice-results.json`.
For BF16, Q4_K_M, and IQ2_M, run the 32K execution/prefill screen as follows:
```bash
for quant in BF16 Q4_K_M IQ2_M; do
llama.cpp/build/bin/llama-perplexity \
--model "Ling-3.0-tiny-${quant}.gguf" \
--file validation/wikitext-2-raw/wiki.test.raw \
--chunks 1 --ctx-size 32768 --batch-size 4096 --ubatch-size 512 \
--gpu-layers all --threads 16 --threads-batch 16 --device CUDA0 \
--split-mode none --main-gpu 0 --fit off --flash-attn off \
--kv-offload --op-offload --no-repack \
--cache-type-k f16 --cache-type-v f16 \
--no-warmup --offline --no-log-timestamps \
> "validation/context-32768-${quant}.log" 2>&1
done
```
This is not exhaustive validation of the native 131K limit.
## 8. Server/Jinja and final integrity checks
The exact four OpenAI-compatible server requests and their expected semantic
checks are stored in `validation/server-requests.json` and
`validation/server-results.json`. Start Q4_K_M with `llama-server --jinja`,
submit each request to `/v1/chat/completions`, and verify thinking on/off,
Chinese generation, normal EOS stops, and the two-argument required tool call.
```bash
llama.cpp/build/bin/llama-server \
--model Ling-3.0-tiny-Q4_K_M.gguf --alias ling-3.0-tiny \
--host 127.0.0.1 --port 8080 --jinja --ctx-size 8192 \
--gpu-layers 999 --split-mode none --main-gpu 0 --device CUDA0 \
--flash-attn off --no-warmup --offline
# In another terminal after the server reports that it is listening:
python validation/run_server_requests.py \
--requests validation/server-requests.json \
--response-dir validation
python validation/validate_server_responses.py \
--requests validation/server-requests.json \
--response-dir validation \
--output validation/server-results.json
```
Finally, verify every downloaded release artifact from the repository root:
```bash
sha256sum -c SHA256SUMS
```
`conversion_manifest.json` records the exact source/control hashes, build and
binary hashes, package versions, hardware, calibration order, artifact tensor
inventories, Hub introduction commits, and validation-report bindings.