[New quant] Q3_PT#19941
Conversation
|
(funny sidenote before any conspiracy theories abound: this quant was originally labeled as IQ3_KL back when the idea was a bit different, but Some Nice People pointed out to me that Somehere Out There there's already a quantization scheme with that name, so I renamed it to avoid any misconceptions) |
|
How are you determining the tables per tensor? Is it a greedy algorithm? Also did you do any tests against pre-existing data types to determine whether per-tensor tables are actually better than the static ones we are currently using? |
I'd lie if I said I understand the algorithm perfectly, since I was basically doing a prototyping workshop session with various assistants and my math background for it is, well, suboptimal :) The basis for it is a weighted Lloyd-Max (minimized mean-square error). We normalize values within subblocks, throw them into 8192 bins (gives virtually perfect quality while still being a noticeable speedup on the biggest tensors, probably could decrease to like 2048 but it's still very fast so didn't bother) and we do 300 iterations, stopping early if we have convergence. I tried to do a comparison but because the block shape is different than with existing quantization schemes you can't really do a direct one. Note that I'm by no means close to an expert on this, so just throwing this out to potentially start a discussion on new optimized quant types. |
|
However, if you're interested, I could try doing specifically one of the existing quants with a per-tensor scale construction to see what the relative KLD would be. |
Yes, I would like to know whether that would be beneficial. |
| { "Q2_K", LLAMA_FTYPE_MOSTLY_Q2_K, " 2.96G, +3.5199 ppl @ Llama-3-8B", }, | ||
| { "Q2_K_S", LLAMA_FTYPE_MOSTLY_Q2_K_S, " 2.96G, +3.1836 ppl @ Llama-3-8B", }, | ||
| { "IQ3_XXS", LLAMA_FTYPE_MOSTLY_IQ3_XXS, " 3.06 bpw quantization", }, | ||
| { "Q3_PT", LLAMA_FTYPE_MOSTLY_Q3_PT, " 3.25 bpw quantization", }, |
There was a problem hiding this comment.
How can it be 3.25 bpw if the type is 3.875 bpw?
There was a problem hiding this comment.
There are a ton of artifacts here because the first experiment was a completely different quant that turned out to not work :)
| } else if (strcmp(argv[arg_idx], "--keep-split") == 0) { | ||
| params.keep_split = true; | ||
| } else if (strcmp(argv[arg_idx], "--keep-split") == 0) { | ||
| params.keep_split = true; |
There was a problem hiding this comment.
This duplicates an existing argument. Likely a merge artifact? Or was this meant to handle --threads?
| printf(" --threads n\n"); | ||
| printf(" number of threads to use for cross-tensor parallelization (default: 0, use same as within-tensor)\n"); | ||
| printf(" when n > 0, enables parallel quantization of multiple tensors simultaneously\n"); |
There was a problem hiding this comment.
This argument is not handled. I assume this isn't intended?
There was a problem hiding this comment.
Yeah, I abandoned this, it's also out-of-scope for this PR.
| // Determine whether this tensor will be Q3_PT (mirror the pass-2 logic) | ||
| bool quantize = tname.rfind("weight") == tname.size() - 6; | ||
| quantize &= (ggml_n_dims(tensor) >= 2); | ||
| quantize &= tname.find("_norm.weight") == std::string::npos; | ||
| quantize &= tname.find("ffn_gate_inp.weight") == std::string::npos; | ||
| if (!quantize) { continue; } |
There was a problem hiding this comment.
This doesn't fully mirror the logic for excluding things to quantize, meaning it will break for recurrent models (like Mamba, RWKV, and others) where some tensors are 2D+, but shouldn't be quantized (so this will produce extra metadata and unnecessary calculations for tensors which aren't quantized to Q3_PT).
Ideally that logic should be in a single place to make it easier to modify, but I think that will be handled by #19770 with its tensor_allows_quantization function.
| case LLAMA_FTYPE_MOSTLY_IQ2_S: return "IQ2_S - 2.5 bpw"; | ||
| case LLAMA_FTYPE_MOSTLY_IQ2_M: return "IQ2_M - 2.7 bpw"; | ||
| case LLAMA_FTYPE_MOSTLY_IQ3_XS: return "IQ3_XS - 3.3 bpw"; | ||
| case LLAMA_FTYPE_MOSTLY_Q3_PT: return "Q3_PT - 3.25 bpw"; |
There was a problem hiding this comment.
The type is 3.875 bpw, no?
I think a less confusing explanation would be that the mappings of representable values of current quants are static, while you're proposing a non-linear quant with dynamic levels/steps depending on the tensor (at least from what I understand). |
Yeah, the original version used the name "codebooks" which was maybe better (but also not fully correct). |
|
@JohannesGaessler aight, here you go :) pure 3.74 bpw quant comparison:
I've committed the reference code for Q3_KPT as well. |
| GGML_TABLE_BEGIN(uint8_t, kmask_iq2xs, 8) | ||
| 1, 2, 4, 8, 16, 32, 64, 128 | ||
| GGML_TABLE_END() | ||
|
|
||
| GGML_TABLE_BEGIN(uint8_t, ksigns_iq2xs, 128) | ||
| 0, 129, 130, 3, 132, 5, 6, 135, 136, 9, 10, 139, 12, 141, 142, 15, | ||
| 144, 17, 18, 147, 20, 149, 150, 23, 24, 153, 154, 27, 156, 29, 30, 159, | ||
| 160, 33, 34, 163, 36, 165, 166, 39, 40, 169, 170, 43, 172, 45, 46, 175, | ||
| 48, 177, 178, 51, 180, 53, 54, 183, 184, 57, 58, 187, 60, 189, 190, 63, | ||
| 192, 65, 66, 195, 68, 197, 198, 71, 72, 201, 202, 75, 204, 77, 78, 207, | ||
| 80, 209, 210, 83, 212, 85, 86, 215, 216, 89, 90, 219, 92, 221, 222, 95, | ||
| 96, 225, 226, 99, 228, 101, 102, 231, 232, 105, 106, 235, 108, 237, 238, 111, | ||
| 240, 113, 114, 243, 116, 245, 246, 119, 120, 249, 250, 123, 252, 125, 126, 255, | ||
| GGML_TABLE_END() |
There was a problem hiding this comment.
I think we misunderstood each other. I understood your idea was to replace these tables with per-tensor tables.
There was a problem hiding this comment.
Ah, now I get it 😀 aight, will try that too.
There was a problem hiding this comment.
(regarding this particular table)
The ksigns don't actually require a table; the most significant bit is derived from how many other bits are set; there can only be an even number of negative numbers per block in those types. That particular table is likely for speed optimization, but isn't technically necessary.
llama.cpp/ggml/src/ggml-quants.c
Lines 3077 to 3096 in ecbcb7e
From what I understand, the per-tensor tables would replace tables like kvalues_iq4nl.
There was a problem hiding this comment.
Ah, now I get it 😀 aight, will try that too.
Well the more pressing matter is that I don't understand what it is your "per-tensor" format is actually doing from your textual description.
There was a problem hiding this comment.
All right, I'll try to explain based on the difference between Q3_K and Q3_KPT.
In Q3_K, you have (in dequantize_row_q3_K):
*y++ = dl * ((int8_t)((q[l+ 0] >> shift) & 3) - ((hm[l+ 0] & m) ? 0 : 4));Here, q is the low bits part of the weight (2 bits) and hm is the high bits (sign) part (1 bit), this gets multiplied by the scale (which is the superblock scale times the respective small block scale).
All of this uses values from a static distribution of [-4, -3, -2, -1, 0, 1, 2, 3]. Those are the only values that can be the result of this internal computation of ((int8_t)((q[l+ 0] >> shift) & 3) - ((hm[l+ 0] & m) ? 0 : 4)). Thus, the distribution of values inside the tensor is constrained by the interaction of the superblock scale and subblock scales. Once those are fixed, the specific quants can only pick an integer multiplier from -4 to 3.
Q3_KPT uses this:
int k_idx = ((q[l + 0] >> shift) & 3) + ((hm[l + 0] & m) ? 4 : 0);
y[l + 0] = dl1 * (levels[k_idx] * 7.0f - 4.0f);where levels is a float array. So, the distribution of values before scaling is also in the range of [-3, 3] - however, the exact distribution is calculated per tensor to account for that tensor's specific distribution of values. Therefore, instead of having [-4, -3, -2, -1, 0, 1, 2, 3], you might have for example [-3, -1, -0.6, 0, 0.3, 0.6, 1, 3] to account for a symmetric distribution that's more likely to have extreme values and values closer to zero - or even [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5] for a tensor that has only zero-or-positive values since it's a waste of precision to then use negative values at all.
Does this make any sense? :) (note: this is also a self-study exercise to me, I strongly believe in learning-by-doing, so please excuse if I'm saying something wrong)
EDIT: @compilade rightly mentioned that the values are from -4 to 3, I got confused by some "super-symetric" quant schemes :)
There was a problem hiding this comment.
Of course as @compilade suggested using float arrays is an efficiency problem, but you can also get a bigger resolution by using int_8 values with a divisor instead, since picking 2^bpw values from 256 is still better resolution than using fixed a fixed preselected set of 2^bpw values.
There was a problem hiding this comment.
To be clear I suggested (elsewhere) to use int8 values for the q3pt_values, to allow using int8 dot products within the sub-blocks, like the other non-linear quants already do.
There was a problem hiding this comment.
Shouldn't Q3_K and Q3_KPT then have the same size though? Or did you use a different format as the base?
There was a problem hiding this comment.
Q3_K and Q3_KPT do have exactly the same size. The original quant in this thread (Q3_PT) was a result of my attempt to make a completely new quant around 3.25bpw which went haywire and produced a completely new quant as a result, but then since you asked how this would work with existing quant I made a "PT" version of Q3_K (same block structure even, the only difference is in how the dequant algorithm uses the per-tensor scales).
There was a problem hiding this comment.
As in:
-a---- 27.02.2026 19:16 1887009952 Qwen_Qwen3-4B-Instruct-2507-Q3_KPT_pure.gguf
-a---- 27.02.2026 18:03 1886997184 Qwen_Qwen3-4B-Instruct-2507-Q3_K_pure.gguf
The difference in size is exactly the per-tensor distributions which are stored in an array in the GGUF header.
|
Just trying to run this against an existing Q8_0 quant (which I understand would yield particularly lossy results) and seeing a segmentation fault. Any idea why that's happening? command line tail of output |
|
@narinishi will check (but keep in mind this is a very draft solution) |
|
@narinishi sorry, couldn't reproduce - you should've gotten a warning that you need an imatrix for this quntization, after making an imatrix the quants worked fine for me. |
|
@pwilkin since you‘re storing additional per-tensor table in metadata, I wonder if you could make it a bit more flexible by allowing multiple tables per tensor. It could be simply a list of tables along with a stride. That way separate tables for merged qkv or experts would be possible. Or even say a table per N rows… |
Yes, that's the next step - but before that, I want to take a step back and see if actually porting reasonably fast kernels for those tensors is possible. No use having a quantization scheme that takes KLD/PPL off if it's super slow. |
I tried using Bartowski's Anything else I might be missing? @pwilkin |
|
@narinishi I'll try using that exact quant and see if I can reproduce. |
|
Now in the quant laboratory: Q4_DPT. Which is just a fancy name for IQ4_NL with learned kvalues. I hacked an extremely ugly CUDA kernel hack to compare the performance effect: (venv) ilintar@LinuksowaJaskinia:/media/ilintar/D_SSD/models$ llama-bench -m Qwen_Qwen3-4B-Instruct-2507-IQ4_NL-pure.gguf
load_backend: loaded BLAS backend from /devel/tools/llama.cpp/build/bin/libggml-blas.so
ggml_cuda_init: found 2 CUDA devices:
Device 0: NVIDIA GeForce RTX 3080, compute capability 8.6, VMM: yes
Device 1: NVIDIA GeForce RTX 5060 Ti, compute capability 12.0, VMM: yes
load_backend: loaded CUDA backend from /devel/tools/llama.cpp/build/bin/libggml-cuda.so
load_backend: loaded CPU backend from /devel/tools/llama.cpp/build/bin/libggml-cpu-haswell.so
| model | size | params | backend | threads | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | ------: | --------------: | -------------------: |
| qwen3 4B IQ4_NL - 4.5 bpw | 2.20 GiB | 4.02 B | BLAS,CUDA | 8 | pp512 | 5394.70 ± 232.97 |
| qwen3 4B IQ4_NL - 4.5 bpw | 2.20 GiB | 4.02 B | BLAS,CUDA | 8 | tg128 | 121.56 ± 0.42 |
build: e395d080c (8173)
(venv) ilintar@LinuksowaJaskinia:/media/ilintar/D_SSD/models$ llama-bench -m Qwen_Qwen3-4B-Instruct-2507-Q4_DPT-pure.gguf
load_backend: loaded BLAS backend from /devel/tools/llama.cpp/build/bin/libggml-blas.so
ggml_cuda_init: found 2 CUDA devices:
Device 0: NVIDIA GeForce RTX 3080, compute capability 8.6, VMM: yes
Device 1: NVIDIA GeForce RTX 5060 Ti, compute capability 12.0, VMM: yes
load_backend: loaded CUDA backend from /devel/tools/llama.cpp/build/bin/libggml-cuda.so
load_backend: loaded CPU backend from /devel/tools/llama.cpp/build/bin/libggml-cpu-haswell.so
| model | size | params | backend | threads | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | ------: | --------------: | -------------------: |
| qwen3 4B Q4_DPT - IQ4_NL with learned levels | 2.20 GiB | 4.02 B | BLAS,CUDA | 8 | pp512 | 5325.80 ± 271.60 |
| qwen3 4B Q4_DPT - IQ4_NL with learned levels | 2.20 GiB | 4.02 B | BLAS,CUDA | 8 | tg128 | 108.28 ± 0.21 |
build: e395d080c (8173)So, not great not terrible I guess. The KLD data for the quants: IQ4_NL:
====== Perplexity statistics ======
Mean PPL(Q) : 8.123314 ± 0.155079
Mean PPL(base) : 7.910885 ± 0.149572
Cor(ln(PPL(Q)), ln(PPL(base))): 99.22%
Mean ln(PPL(Q)/PPL(base)) : 0.026499 ± 0.002381
Mean PPL(Q)/PPL(base) : 1.026853 ± 0.002445
Mean PPL(Q)-PPL(base) : 0.212429 ± 0.019811
====== KL divergence statistics ======
Mean KLD: 0.033115 ± 0.000551
Maximum KLD: 4.302760
99.9% KLD: 1.215412
99.0% KLD: 0.304676
95.0% KLD: 0.121186
90.0% KLD: 0.077289
Median KLD: 0.011778
10.0% KLD: 0.000026
5.0% KLD: 0.000003
1.0% KLD: -0.000000
0.1% KLD: -0.000004
Minimum KLD: -0.000004
====== Token probability statistics ======
Mean Δp: -0.522 ± 0.036 %
Maximum Δp: 86.560%
99.9% Δp: 36.079%
99.0% Δp: 15.386%
95.0% Δp: 6.054%
90.0% Δp: 2.912%
75.0% Δp: 0.234%
Median Δp: -0.001%
25.0% Δp: -0.724%
10.0% Δp: -4.718%
5.0% Δp: -9.085%
1.0% Δp: -20.438%
0.1% Δp: -47.195%
Minimum Δp: -86.191%
RMS Δp : 5.767 ± 0.103 %
Same top p: 91.992 ± 0.170 %
Q4_DPT:
====== Perplexity statistics ======
Mean PPL(Q) : 8.169248 ± 0.156889
Mean PPL(base) : 7.910885 ± 0.149572
Cor(ln(PPL(Q)), ln(PPL(base))): 99.28%
Mean ln(PPL(Q)/PPL(base)) : 0.032137 ± 0.002301
Mean PPL(Q)/PPL(base) : 1.032659 ± 0.002376
Mean PPL(Q)-PPL(base) : 0.258363 ± 0.019745
====== KL divergence statistics ======
Mean KLD: 0.030403 ± 0.000630
Maximum KLD: 8.150362
99.9% KLD: 1.044685
99.0% KLD: 0.282885
95.0% KLD: 0.108604
90.0% KLD: 0.068070
Median KLD: 0.010477
10.0% KLD: 0.000022
5.0% KLD: 0.000003
1.0% KLD: -0.000000
0.1% KLD: -0.000003
Minimum KLD: -0.000004
====== Token probability statistics ======
Mean Δp: -0.354 ± 0.035 %
Maximum Δp: 96.635%
99.9% Δp: 33.628%
99.0% Δp: 14.918%
95.0% Δp: 6.130%
90.0% Δp: 3.110%
75.0% Δp: 0.296%
Median Δp: -0.000%
25.0% Δp: -0.546%
10.0% Δp: -4.105%
5.0% Δp: -7.891%
1.0% Δp: -20.029%
0.1% Δp: -48.167%
Minimum Δp: -91.041%
RMS Δp : 5.597 ± 0.110 %
Same top p: 92.459 ± 0.165 % |
|
@JohannesGaessler If you have some free time I'd appreciate you taking a look if there's a civilized way to pass the kvalues over to the CUDA kernels (this is the only reason I've included them for this quant specifically). |
|
My opinion was and still is that just like with FP8 the per-tensor auxiliary data should live in an optional dedicated |
|
@JohannesGaessler okay, so my idea for doing that based on your input right now looks like this: -> make a new llm_graph_input_i subclass for the per-quantization-type levels Does this make sense? |
d922579 to
4de5db4
Compare
DeepSeek4 stores attn_output_a (wo_a) as a 2D weight but reshapes it into
an o_groups grouped matmul at inference. This broke imatrix twice:
1. Naming: ggml_reshape_3d renames the view "<w> (reshaped)", and
filter_tensor_name only stripped the backend#..#split wrapper, so the
importance stats were saved under "...weight (reshaped)" and never
matched at quant time ("Missing importance matrix"). Now also strip
trailing (reshaped)/(view)/(cont)/(transposed)/(permuted) annotations.
2. Shape: the collected imatrix is one vector per group, i.e. an integer
multiple of ne[0]*ne[2], which tripped the size check ("imatrix size
different"). The quantizer now accepts a size that is a whole multiple
of the per-slice size, treats it as size/ne[0] contiguous row-groups,
and quantizes each group with its own vector. This is a strict
generalization of the per-ne[2]-slice loop: expert (ne[2]>1) and
normal (size==ne[0]) cases are byte-identical to before. No model or
GGUF metadata change; wo_a stays a normal 2D quantized tensor.
Also adds fix_reshaped_imatrix.py to recover imatrix files computed before
fix (1) by stripping the annotations from the stored tensor keys.
Assisted-By: Claude Opus 4.8 <noreply@anthropic.com>
The pass-2 quantizer already applies a grouped imatrix per row-group for weights like DeepSeek4 attn_output_a. The pass-1 per-tensor trainers (Q3_PT/Q3_KPT/Q4_DPT/Q2_KPT/IQ2_TQ/IQ3_TQ/IQ1_BN) still gated the imatrix on size == ne[0]*ne[2], so for a grouped weight they silently trained levels/grids/codebooks without any importance weighting. Route all seven resolutions through a shared resolve_level_imatrix() helper: when the stored imatrix holds multiple groups over a 2D weight, average the group vectors into one per-column vector for these single-vector trainers; otherwise return the data unchanged. This is a strict generalization - the per-slice (size == ne[0]*ne[2]) and genuine-mismatch cases behave exactly as before. Assisted-By: Claude Opus 4.8 <noreply@anthropic.com>
DeepSeek4 MoE routing tables (ffn_gate_tid2eid / ffn_gate_eid2tid) are i32 id maps, not weights. Their names end in ".weight" and they are 2D, so the quantize gate selected them and quantization then failed with "cannot dequantize/convert tensor type i32". Skip any I8/I16/I32/I64 tensor in tensor_allows_quantization (pass-2) and in the pass-1 trainer gates, so these tensors are copied through unchanged. Assisted-By: Claude Opus 4.8 <noreply@anthropic.com>
The seven pass-1 per-tensor trainers (Q3_PT/Q3_KPT/Q4_DPT/Q2_KPT/IQ2_TQ/ IQ3_TQ/IQ1_BN) each repeated ~40 lines of identical setup: the quantize gate, target-type resolution, tensor load, f32 dequantize, and imatrix resolution. Extract this into a reusable pass1_setup driver with a prepare(tensor, want_type) method that returns the f32 data, resolved (possibly grouped) imatrix, name and dims, so each loop keeps only its genuinely type-specific training call. Net -237 lines. This also unifies the target-type resolution: Q3_PT/Q3_KPT/Q2_KPT/IQ1_BN were still using divergent inline copies (Q3_PT even omitted the token_embedding_type/output_tensor_type overrides, and none honored the --tensor-type regex patterns). They now all go through resolve_tensor_type like Q4_DPT/IQ2_TQ/IQ3_TQ already did, matching pass-2. Verified with a Q3_PT quantize of DeepSeek4-Flash: pass-1 trains every tensor (including grouped attn_output_a) and pass-2 runs, with no errors. Assisted-By: Claude Opus 4.8 <noreply@anthropic.com>
blk.N.attn_compressor_ape / indexer_compressor_ape are absolute position
embedding tables, added as a positional bias via get_rows/add rather than
used as matmul weights, so the imatrix carries no data for them ("did not
find weights ..."). At a very low-bit target that requires imatrix this
would then abort with "Missing importance matrix".
They are tiny position tables, so skip them in quantization (both the
pass-2 gate and the pass-1 trainer gate), mirroring the existing
.position_embd handling. They pass through at their original precision.
Assisted-By: Claude Opus 4.8 <noreply@anthropic.com>
- arm: forward the new `levels` arg in the q1_0 generic fallback call - arch-fallback: add missing q4_dpt/q2_dpt generic renames for loongarch (no native impl -> undefined symbol), and drop the erroneous q3_pt rename for wasm (native impl present -> duplicate symbol) - test-quantize-fns: pass the new `levels` arg (nullptr) to f32 vec_dot Assisted-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ggml-quants.c is part of ggml-base and is compiled on all platforms, but the IQ1_BN k-means code used raw <pthread.h>, which does not exist on Windows/MSVC (fatal error: 'pthread.h' file not found on the arm64/x64 windows-llvm builds). Add the same thin Win32 pthread shim ggml-cpu.c uses (HANDLE-based pthread_create/pthread_join, DWORD thread_ret_t) behind an #if _WIN32 guard, and give the k-means workers the portable thread_ret_t return type. POSIX keeps using pthreads unchanged. Assisted-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes surfaced by the ARM, HIP, MUSA, CUDA, OpenCL and Windows CI jobs:
- arm/quants.c: drop a duplicate, misnamed copy of the q1_0 kernel that
was defined as ggml_vec_dot_q4_0_q8_0 and collided with the real q4_0
(redefinition + unused-parameter errors on every ARM build)
- wasm/quants.c: GGML_UNUSED(levels) in q4_0 (-Werror=unused-parameter)
- ggml-quants.c: clamp the k-means thread count to n_samples before
flooring to 1 so nthread is provably >= 1 and calloc() no longer trips
-Werror=alloc-size-larger-than on gcc
- ggml-cuda/common.cuh: use -FLT_MAX / -HALF_MAX instead of -INFINITY for
the MAX block-reduce sentinel (-INFINITY is rejected under the HIP
-ffast-math build via -Werror,-Wnan-infinity-disabled)
- ggml-cuda/convert.cu: GGML_UNUSED(n_levels) in ggml_cuda_set_q2kpt_levels
- ggml-cuda/vendors/{hip,musa}.h: map cudaMemcpyToSymbolAsync to the
hip/musa equivalents so convert.cu and mmvq.cu build on HIP and MUSA
- ggml-opencl.cpp: pass the new levels arg (nullptr) to the to_float trait
- auto-tensor-type: define strcasecmp -> _stricmp on Windows (MSVC/clang)
Assisted-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Inspired but also a bit worried by the discussions over the recent quant proposals, I've decided to try my luck and spent the last few days chugging my AI workhorses to prototype a new quant for llama.cpp. The idea behind the quant was pretty simple: all of the quants in llama.cpp use static, predefined "magic scales" that were determined to be somewhat optimal for quantizing tensors. So I thought - how about we instead create per-tensor scales pre-quantization and then use them for quantizing and dequantizing? That's how the Q3_PT (or "Per Tensor") quantization scheme was born.
I've tested this quant on Qwen3-4B-Instruct-2507. The imatrix dataset is @bartowski1182 's https://gist.github.com/bartowski1182/82ae9b520227f57d79ba04add13d0d0d, while the KLD and perplexity testing dataset used is @EAddario 's https://huggingface.co/datasets/eaddario/imatrix-calibration
combined_all_smallwith 100 chunks.This PR contains, per the recommendations, just the pure CPU reference code and quantization/dequantization code, so obviously any inference using this quant will be painfully slow. However, I've made the KLD and perplexity tests and they show this quant very nicely fills a gap between the Q3_K and IQ4_XS quant types. The "pure" quants mean that they were quantized with
--pure, the only exceptions being the embedding and output tensors quantized inQ6_K.