2023-09-15 16:59:49 -04:00
# include "common.h"
2023-05-20 11:06:11 +03:00
# include "llama.h"
2025-02-09 12:06:15 -05:00
# include "gguf.h"
2023-05-20 11:06:11 +03:00
2023-03-10 20:40:58 +02:00
# include <cstdio>
2023-06-10 01:59:17 -06:00
# include <cstring>
2023-06-13 04:23:23 -06:00
# include <vector>
2023-03-10 20:40:58 +02:00
# include <string>
2024-01-14 09:45:56 +02:00
# include <unordered_map>
2024-09-10 11:31:49 -04:00
# include <map>
2024-01-14 09:45:56 +02:00
# include <fstream>
# include <cmath>
2025-03-04 17:53:26 +01:00
# include <cctype>
2025-04-13 19:29:28 +01:00
# include <algorithm>
2023-03-21 14:21:50 -03:00
2023-06-13 04:23:23 -06:00
struct quant_option {
std : : string name ;
llama_ftype ftype ;
std : : string desc ;
2023-04-26 18:43:27 +02:00
} ;
2025-04-13 19:29:28 +01:00
static const std : : vector < quant_option > QUANT_OPTIONS = {
2024-07-16 10:00:30 +03:00
{ " Q4_0 " , LLAMA_FTYPE_MOSTLY_Q4_0 , " 4.34G, +0.4685 ppl @ Llama-3-8B " , } ,
{ " Q4_1 " , LLAMA_FTYPE_MOSTLY_Q4_1 , " 4.78G, +0.4511 ppl @ Llama-3-8B " , } ,
{ " Q5_0 " , LLAMA_FTYPE_MOSTLY_Q5_0 , " 5.21G, +0.1316 ppl @ Llama-3-8B " , } ,
{ " Q5_1 " , LLAMA_FTYPE_MOSTLY_Q5_1 , " 5.65G, +0.1062 ppl @ Llama-3-8B " , } ,
{ " IQ2_XXS " , LLAMA_FTYPE_MOSTLY_IQ2_XXS , " 2.06 bpw quantization " , } ,
{ " IQ2_XS " , LLAMA_FTYPE_MOSTLY_IQ2_XS , " 2.31 bpw quantization " , } ,
{ " IQ2_S " , LLAMA_FTYPE_MOSTLY_IQ2_S , " 2.5 bpw quantization " , } ,
{ " IQ2_M " , LLAMA_FTYPE_MOSTLY_IQ2_M , " 2.7 bpw quantization " , } ,
{ " IQ1_S " , LLAMA_FTYPE_MOSTLY_IQ1_S , " 1.56 bpw quantization " , } ,
{ " IQ1_M " , LLAMA_FTYPE_MOSTLY_IQ1_M , " 1.75 bpw quantization " , } ,
ggml-quants : ternary packing for TriLMs and BitNet b1.58 (#8151)
* ggml-quants : 1.625 bpw ternary packing for BitNet 1.58b
* ggml-quants : faster 1.625 bpw AVX2 vec_dot
Not using a lookup table anymore makes it match q4_0 speed.
* gguf-py : fix formatting
* llama : remove spaces on empty line
* ggml-quants : subtract 1 when back in epi8
This makes the 1.625 bpw type go faster than q4_0. Still not the fastest.
* ggml-quants : Q2_2 now faster than Q4_K on with AVX2
* ggml-quants : cleanup Q1_3 code formatting
* ggml-quants : ARM NEON vec_dot for q2_2 and q1_3
* ggml-quants : use ceiling division when quantizing q1_3
* convert-hf : simplify BitNet pre-quantization
This still results in the exact same tensor weights and scales,
but it reveals some weirdness in the current algorithm.
* convert-hf : allow converting the weird BitNet 1.3B
Its FFN size is 5460 which is not convenient.
The offending tensors are kept in F16,
which makes the final model 5.01 bpw.
* bitnet : replace 1.58b with b1.58, as in the paper
* ggml-quants : fix build failure on Windows
* ggml-quants : attempt to fix Arm 32-bit support
* ggml : add some informative comments in q1_3 vec_dot
* ggml : add TQ1_0 and TQ2_0 ternary quantization types
* ggml : even faster TQ2_0
* ggml : also faster TQ1_0
Same optimization as for TQ2_0 by offsetting the sum instead of the weights.
This makes TQ1_0 almost as fast as Q8_0 on AVX2.
* ggml : fix build issues in certain environments
* ggml : add NEON vec_dot implementation for TQ1_0 and TQ2_0
* ggml : avoid directly using vmlal_high_s8, for 32-bit ARM compat
The compiler seems smart enough to use the same instruction
even when using vget_high_s8 instead.
* ggml : remove q1_3 and q2_2
No more 1.625 bpw and 2.000 bpw,
now instead using 1.6875 bpw and 2.0625 bpw
with TQ1_0 and TQ2_0, respectively.
* llama : remove the separate scale tensors of BitNet b1.58
They won't be needed, since the remaining ternary quant types have
built-in scales.
* ggml-quants : rename fields of TQ1_0 and TQ2_0 structs for consistency
* ggml-quants : allow using vdotq_s32 in TQ2_0 vec_dot
Not yet tested on hardware which supports it,
might not work or might not even compile. But also it might.
It should make the performance better on recent ARM CPUs.
* ggml-quants : remove comment about possible format change of TQ2_0
Making it slightly more convenient for AVX512
but less convenient for everything else is not worth the trouble.
* gguf-py : Numpy (de)quantization for TQ1_0 and TQ2_0
* ggml-quants : use roundf instead of nearest_int for TQ1_0 and TQ2_0
This does not change anything for ternary models,
since their values should never end up being in halfway cases anyway.
* convert : allow direct conversion to TQ1_0 and TQ2_0
The token embeddings and output tensors are kept in F16
to allow quantizing them to Q4_K and Q6_K with llama-quantize.
* llama : handle fallback for TQ1_0 and TQ2_0 with Q4_0
Q4_0 is not completely symmetric (so not lossless for ternary models),
but it should be good enough.
* ggml-quants : allow using ARM dot product instructions for TQ1_0
* ggml-quants : deduplicate TQ1_0 and TQ2_0 __ARM_FEATURE_DOTPROD support
* ggml : remove unused ggml_mul special case
It would otherwise conflict with the more general
optimization coming with Mamba-2.
* ggml : handle TQ1_0 and TQ2_0 in dequantization-based operators
* test-backend-ops : add TQ1_0 and TQ2_0 comments for later
Not yet adding uncommented, because some backends like SYCL and Metal
do not properly handle unknown types in supports_op for GGML_OP_MUL_MAT.
(and Metal also doesn't handle it with GGML_OP_GET_ROWS)
Support for TQ1_0 and TQ2_0 for other backends than CPU
will be added in follow-up pull requests.
2024-09-05 21:48:47 -04:00
{ " TQ1_0 " , LLAMA_FTYPE_MOSTLY_TQ1_0 , " 1.69 bpw ternarization " , } ,
{ " TQ2_0 " , LLAMA_FTYPE_MOSTLY_TQ2_0 , " 2.06 bpw ternarization " , } ,
2024-07-16 10:00:30 +03:00
{ " 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 " , } ,
{ " IQ3_S " , LLAMA_FTYPE_MOSTLY_IQ3_S , " 3.44 bpw quantization " , } ,
{ " IQ3_M " , LLAMA_FTYPE_MOSTLY_IQ3_M , " 3.66 bpw quantization mix " , } ,
{ " Q3_K " , LLAMA_FTYPE_MOSTLY_Q3_K_M , " alias for Q3_K_M " } ,
{ " IQ3_XS " , LLAMA_FTYPE_MOSTLY_IQ3_XS , " 3.3 bpw quantization " , } ,
{ " Q3_K_S " , LLAMA_FTYPE_MOSTLY_Q3_K_S , " 3.41G, +1.6321 ppl @ Llama-3-8B " , } ,
{ " Q3_K_M " , LLAMA_FTYPE_MOSTLY_Q3_K_M , " 3.74G, +0.6569 ppl @ Llama-3-8B " , } ,
{ " Q3_K_L " , LLAMA_FTYPE_MOSTLY_Q3_K_L , " 4.03G, +0.5562 ppl @ Llama-3-8B " , } ,
{ " IQ4_NL " , LLAMA_FTYPE_MOSTLY_IQ4_NL , " 4.50 bpw non-linear quantization " , } ,
{ " IQ4_XS " , LLAMA_FTYPE_MOSTLY_IQ4_XS , " 4.25 bpw non-linear quantization " , } ,
{ " Q4_K " , LLAMA_FTYPE_MOSTLY_Q4_K_M , " alias for Q4_K_M " , } ,
{ " Q4_K_S " , LLAMA_FTYPE_MOSTLY_Q4_K_S , " 4.37G, +0.2689 ppl @ Llama-3-8B " , } ,
{ " Q4_K_M " , LLAMA_FTYPE_MOSTLY_Q4_K_M , " 4.58G, +0.1754 ppl @ Llama-3-8B " , } ,
{ " Q5_K " , LLAMA_FTYPE_MOSTLY_Q5_K_M , " alias for Q5_K_M " , } ,
{ " Q5_K_S " , LLAMA_FTYPE_MOSTLY_Q5_K_S , " 5.21G, +0.1049 ppl @ Llama-3-8B " , } ,
{ " Q5_K_M " , LLAMA_FTYPE_MOSTLY_Q5_K_M , " 5.33G, +0.0569 ppl @ Llama-3-8B " , } ,
{ " Q6_K " , LLAMA_FTYPE_MOSTLY_Q6_K , " 6.14G, +0.0217 ppl @ Llama-3-8B " , } ,
{ " Q8_0 " , LLAMA_FTYPE_MOSTLY_Q8_0 , " 7.96G, +0.0026 ppl @ Llama-3-8B " , } ,
{ " F16 " , LLAMA_FTYPE_MOSTLY_F16 , " 14.00G, +0.0020 ppl @ Mistral-7B " , } ,
{ " BF16 " , LLAMA_FTYPE_MOSTLY_BF16 , " 14.00G, -0.0050 ppl @ Mistral-7B " , } ,
{ " F32 " , LLAMA_FTYPE_ALL_F32 , " 26.00G @ 7B " , } ,
2023-09-01 08:02:48 -06:00
// Note: Ensure COPY comes after F32 to avoid ftype 0 from matching.
2024-07-16 10:00:30 +03:00
{ " COPY " , LLAMA_FTYPE_ALL_F32 , " only copy tensors, no quantizing " , } ,
2023-06-13 04:23:23 -06:00
} ;
2025-05-13 18:12:31 +01:00
// Quantization types. Changes to this struct must be replicated in llama-quantize.cpp
struct tensor_quantization {
std : : string name ;
ggml_type quant = GGML_TYPE_COUNT ;
} ;
2024-04-26 20:06:33 +02:00
static const char * const LLM_KV_QUANTIZE_IMATRIX_FILE = " quantize.imatrix.file " ;
static const char * const LLM_KV_QUANTIZE_IMATRIX_DATASET = " quantize.imatrix.dataset " ;
static const char * const LLM_KV_QUANTIZE_IMATRIX_N_ENTRIES = " quantize.imatrix.entries_count " ;
static const char * const LLM_KV_QUANTIZE_IMATRIX_N_CHUNKS = " quantize.imatrix.chunks_count " ;
2023-06-13 04:23:23 -06:00
2024-09-06 17:17:25 -04:00
// TODO: share with imatrix.cpp
2025-04-15 17:29:57 -04:00
static const char * const LLM_KV_IMATRIX_DATASETS = " imatrix.datasets " ;
2024-09-06 17:17:25 -04:00
static const char * const LLM_KV_IMATRIX_CHUNK_COUNT = " imatrix.chunk_count " ;
static const char * const LLM_KV_IMATRIX_CHUNK_SIZE = " imatrix.chunk_size " ;
2024-09-20 20:55:36 +02:00
static bool striequals ( const char * a , const char * b ) {
while ( * a & & * b ) {
if ( std : : tolower ( * a ) ! = std : : tolower ( * b ) ) {
return false ;
}
a + + ; b + + ;
}
return * a = = * b ;
}
2023-09-15 15:38:27 -04:00
static bool try_parse_ftype ( const std : : string & ftype_str_in , llama_ftype & ftype , std : : string & ftype_str_out ) {
2023-06-13 04:23:23 -06:00
std : : string ftype_str ;
for ( auto ch : ftype_str_in ) {
ftype_str . push_back ( std : : toupper ( ch ) ) ;
}
2025-04-15 17:29:57 -04:00
for ( const auto & it : QUANT_OPTIONS ) {
2024-09-20 20:55:36 +02:00
if ( striequals ( it . name . c_str ( ) , ftype_str . c_str ( ) ) ) {
2023-06-13 04:23:23 -06:00
ftype = it . ftype ;
ftype_str_out = it . name ;
return true ;
}
2023-05-05 00:58:56 +02:00
}
try {
int ftype_int = std : : stoi ( ftype_str ) ;
2025-04-15 17:29:57 -04:00
for ( const auto & it : QUANT_OPTIONS ) {
2023-06-13 04:23:23 -06:00
if ( it . ftype = = ftype_int ) {
ftype = it . ftype ;
ftype_str_out = it . name ;
2023-05-05 00:58:56 +02:00
return true ;
}
}
}
catch ( . . . ) {
// stoi failed
}
return false ;
}
2023-03-10 20:40:58 +02:00
// usage:
2024-08-07 01:43:00 +02:00
// ./llama-quantize [--allow-requantize] [--leave-output-tensor] [--pure] models/llama/ggml-model.gguf [models/llama/ggml-model-quant.gguf] type [nthreads]
2023-03-10 20:40:58 +02:00
//
2023-09-28 17:41:44 -04:00
[[noreturn]]
2023-09-15 15:38:27 -04:00
static void usage ( const char * executable ) {
2025-04-13 19:29:28 +01:00
printf ( " usage: %s [--help] [--allow-requantize] [--leave-output-tensor] [--pure] [--imatrix] [--include-weights] [--exclude-weights] [--output-tensor-type] \n " , executable ) ;
printf ( " [--token-embedding-type] [--tensor-type] [--keep-split] [--override-kv] model-f32.gguf [model-quant.gguf] type [nthreads] \n \n " ) ;
2023-09-01 08:02:48 -06:00
printf ( " --allow-requantize: Allows requantizing tensors that have already been quantized. Warning: This can severely reduce quality compared to quantizing from 16bit or 32bit \n " ) ;
printf ( " --leave-output-tensor: Will leave output.weight un(re)quantized. Increases model size but may also increase quality, especially when requantizing \n " ) ;
2023-10-29 18:32:28 +02:00
printf ( " --pure: Disable k-quant mixtures and quantize all tensors to the same type \n " ) ;
2024-01-14 16:21:12 +02:00
printf ( " --imatrix file_name: use data in file_name as importance matrix for quant optimizations \n " ) ;
2024-01-14 09:45:56 +02:00
printf ( " --include-weights tensor_name: use importance matrix for this/these tensor(s) \n " ) ;
printf ( " --exclude-weights tensor_name: use importance matrix for this/these tensor(s) \n " ) ;
2024-03-26 13:09:30 +01:00
printf ( " --output-tensor-type ggml_type: use this ggml_type for the output.weight tensor \n " ) ;
printf ( " --token-embedding-type ggml_type: use this ggml_type for the token embeddings tensor \n " ) ;
2025-04-13 19:29:28 +01:00
printf ( " --tensor-type TENSOR=TYPE: quantize this tensor to this ggml_type. example: --tensor-type attn_q=q8_0 \n " ) ;
printf ( " Advanced option to selectively quantize tensors. May be specified multiple times. \n " ) ;
2024-08-24 07:22:45 +01:00
printf ( " --keep-split: will generate quantized model in the same shards as input \n " ) ;
2024-03-26 13:09:30 +01:00
printf ( " --override-kv KEY=TYPE:VALUE \n " ) ;
printf ( " Advanced option to override model metadata by key in the quantized model. May be specified multiple times. \n " ) ;
2024-01-14 09:45:56 +02:00
printf ( " Note: --include-weights and --exclude-weights cannot be used together \n " ) ;
2023-09-01 08:02:48 -06:00
printf ( " \n Allowed quantization types: \n " ) ;
2025-04-15 17:29:57 -04:00
for ( const auto & it : QUANT_OPTIONS ) {
2023-09-01 08:02:48 -06:00
if ( it . name ! = " COPY " ) {
printf ( " %2d or " , it . ftype ) ;
} else {
printf ( " " ) ;
}
2024-01-14 09:45:56 +02:00
printf ( " %-7s : %s \n " , it . name . c_str ( ) , it . desc . c_str ( ) ) ;
2023-06-10 01:59:17 -06:00
}
exit ( 1 ) ;
}
2024-09-10 11:31:49 -04:00
// TODO: share with implementation in imatrix.cpp
static bool str_remove_suffix ( std : : string & str , const std : : string & suffix ) {
bool has_suffix = str . size ( ) > = suffix . size ( ) & & str . compare ( str . size ( ) - suffix . size ( ) , str . size ( ) , suffix ) = = 0 ;
if ( has_suffix ) {
str = str . substr ( 0 , str . size ( ) - suffix . size ( ) ) ;
}
return has_suffix ;
}
2025-04-15 17:29:57 -04:00
static int load_legacy_imatrix ( const std : : string & imatrix_file , std : : vector < std : : string > & imatrix_datasets , std : : unordered_map < std : : string , std : : vector < float > > & imatrix_data ) {
std : : ifstream in ( imatrix_file . c_str ( ) , std : : ios : : binary ) ;
if ( ! in ) {
printf ( " %s: failed to open %s \n " , __func__ , imatrix_file . c_str ( ) ) ;
exit ( 1 ) ;
}
int n_entries ;
in . read ( ( char * ) & n_entries , sizeof ( n_entries ) ) ;
if ( in . fail ( ) | | n_entries < 1 ) {
printf ( " %s: no data in file %s \n " , __func__ , imatrix_file . c_str ( ) ) ;
exit ( 1 ) ;
}
for ( int i = 0 ; i < n_entries ; + + i ) {
int len ; in . read ( ( char * ) & len , sizeof ( len ) ) ;
std : : vector < char > name_as_vec ( len + 1 ) ;
in . read ( ( char * ) name_as_vec . data ( ) , len ) ;
if ( in . fail ( ) ) {
printf ( " %s: failed reading name for entry %d from %s \n " , __func__ , i + 1 , imatrix_file . c_str ( ) ) ;
exit ( 1 ) ;
}
name_as_vec [ len ] = 0 ;
std : : string name { name_as_vec . data ( ) } ;
auto & e = imatrix_data [ name ] ;
int ncall ;
in . read ( ( char * ) & ncall , sizeof ( ncall ) ) ;
int nval ;
in . read ( ( char * ) & nval , sizeof ( nval ) ) ;
if ( in . fail ( ) | | nval < 1 ) {
printf ( " %s: failed reading number of values for entry %d \n " , __func__ , i ) ;
imatrix_data = { } ;
exit ( 1 ) ;
}
e . resize ( nval ) ;
in . read ( ( char * ) e . data ( ) , nval * sizeof ( float ) ) ;
if ( in . fail ( ) ) {
printf ( " %s: failed reading data for entry %d \n " , __func__ , i ) ;
imatrix_data = { } ;
exit ( 1 ) ;
}
if ( ncall > 0 ) {
2025-06-23 11:50:54 -04:00
for ( auto & v : e ) {
v / = ncall ;
}
2025-04-15 17:29:57 -04:00
}
if ( getenv ( " LLAMA_TRACE " ) ) {
printf ( " %s: loaded data (size = %6d, ncall = %6d) for '%s' \n " , __func__ , int ( e . size ( ) ) , ncall , name . c_str ( ) ) ;
}
}
2025-06-23 12:43:25 -04:00
// latest legacy imatrix version contains the dataset filename at the end of the file
2025-04-15 17:29:57 -04:00
int m_last_call = 0 ;
if ( in . peek ( ) ! = EOF ) {
in . read ( ( char * ) & m_last_call , sizeof ( m_last_call ) ) ;
int dataset_len ;
in . read ( ( char * ) & dataset_len , sizeof ( dataset_len ) ) ;
std : : vector < char > dataset_as_vec ( dataset_len ) ;
in . read ( dataset_as_vec . data ( ) , dataset_len ) ;
imatrix_datasets . resize ( 1 ) ;
imatrix_datasets [ 0 ] . assign ( dataset_as_vec . begin ( ) , dataset_as_vec . end ( ) ) ;
printf ( " %s: imatrix dataset='%s' \n " , __func__ , imatrix_datasets [ 0 ] . c_str ( ) ) ;
}
printf ( " %s: loaded %d importance matrix entries from %s computed on %d chunks \n " , __func__ , int ( imatrix_data . size ( ) ) , imatrix_file . c_str ( ) , m_last_call ) ;
return m_last_call ;
}
static int load_imatrix ( const std : : string & imatrix_file , std : : vector < std : : string > & imatrix_datasets , std : : unordered_map < std : : string , std : : vector < float > > & imatrix_data ) {
2024-09-06 17:17:25 -04:00
struct ggml_context * ctx = nullptr ;
struct gguf_init_params meta_gguf_params = {
/* .no_alloc = */ false , // the data is needed
/* .ctx = */ & ctx ,
} ;
struct gguf_context * ctx_gguf = gguf_init_from_file ( imatrix_file . c_str ( ) , meta_gguf_params ) ;
if ( ! ctx_gguf ) {
2025-04-15 17:29:57 -04:00
fprintf ( stderr , " %s: imatrix file '%s' is using old format \n " , __func__ , imatrix_file . c_str ( ) ) ;
return load_legacy_imatrix ( imatrix_file , imatrix_datasets , imatrix_data ) ;
2024-01-14 09:45:56 +02:00
}
2024-09-06 17:17:25 -04:00
const int32_t n_entries = gguf_get_n_tensors ( ctx_gguf ) ;
2024-09-10 11:31:49 -04:00
if ( n_entries < 1 ) {
2024-09-06 17:17:25 -04:00
fprintf ( stderr , " %s: no data in file %s \n " , __func__ , imatrix_file . c_str ( ) ) ;
gguf_free ( ctx_gguf ) ;
ggml_free ( ctx ) ;
2024-04-03 15:07:05 +02:00
exit ( 1 ) ;
2024-01-14 09:45:56 +02:00
}
2024-09-06 17:17:25 -04:00
2025-04-15 17:29:57 -04:00
const int dataset_idx = gguf_find_key ( ctx_gguf , LLM_KV_IMATRIX_DATASETS ) ;
2024-09-06 17:17:25 -04:00
const int chunk_count_idx = gguf_find_key ( ctx_gguf , LLM_KV_IMATRIX_CHUNK_COUNT ) ;
const int chunk_size_idx = gguf_find_key ( ctx_gguf , LLM_KV_IMATRIX_CHUNK_SIZE ) ;
if ( dataset_idx < 0 | | chunk_count_idx < 0 | | chunk_size_idx < 0 ) {
fprintf ( stderr , " %s: missing imatrix metadata in file %s \n " , __func__ , imatrix_file . c_str ( ) ) ;
gguf_free ( ctx_gguf ) ;
ggml_free ( ctx ) ;
exit ( 1 ) ;
}
const uint32_t chunk_size = gguf_get_val_u32 ( ctx_gguf , chunk_size_idx ) ;
2025-04-15 17:29:57 -04:00
const std : : string sums_suffix { " .in_sum2 " } ;
const std : : string counts_suffix { " .counts " } ;
2024-09-06 17:17:25 -04:00
2024-09-10 11:31:49 -04:00
// Using an ordered map to get a deterministic iteration order.
std : : map < std : : string , std : : pair < struct ggml_tensor * , struct ggml_tensor * > > sums_counts_for ;
for ( struct ggml_tensor * cur = ggml_get_first_tensor ( ctx ) ; cur ; cur = ggml_get_next_tensor ( ctx , cur ) ) {
std : : string name = cur - > name ;
if ( name . empty ( ) ) { continue ; }
if ( str_remove_suffix ( name , sums_suffix ) ) {
2025-04-15 17:29:57 -04:00
// in_sum2
sums_counts_for [ std : : move ( name ) ] . first = cur ;
2024-09-10 11:31:49 -04:00
} else if ( str_remove_suffix ( name , counts_suffix ) ) {
// counts
2025-04-15 17:29:57 -04:00
sums_counts_for [ std : : move ( name ) ] . second = cur ;
2024-09-10 11:31:49 -04:00
} else {
2025-04-15 17:29:57 -04:00
// ignore other tensors
2024-01-14 09:45:56 +02:00
}
2024-09-10 11:31:49 -04:00
}
for ( const auto & sc : sums_counts_for ) {
const std : : string & name = sc . first ;
const struct ggml_tensor * sums = sc . second . first ;
const struct ggml_tensor * counts = sc . second . second ;
2024-09-06 17:17:25 -04:00
if ( ! sums | | ! counts ) {
2024-09-10 11:31:49 -04:00
fprintf ( stderr , " %s: mismatched sums and counts for %s \n " , __func__ , name . c_str ( ) ) ;
2024-09-06 17:17:25 -04:00
gguf_free ( ctx_gguf ) ;
ggml_free ( ctx ) ;
2024-04-03 15:07:05 +02:00
exit ( 1 ) ;
2024-01-14 09:45:56 +02:00
}
2024-04-03 15:07:05 +02:00
2024-09-06 17:17:25 -04:00
const int64_t ne0 = sums - > ne [ 0 ] ;
const int64_t ne1 = sums - > ne [ 1 ] ;
2024-09-10 11:31:49 -04:00
2024-09-06 17:17:25 -04:00
auto & e = imatrix_data [ name ] ;
e . resize ( ggml_nelements ( sums ) ) ;
float max_count = 0.0f ;
for ( int64_t j = 0 ; j < ne1 ; + + j ) {
2024-09-08 10:04:01 -04:00
const float count = ( ( const float * ) counts - > data ) [ j ] ;
2025-04-15 17:29:57 -04:00
if ( count > 0.0f ) {
for ( int64_t i = 0 ; i < ne0 ; + + i ) {
e [ j * ne0 + i ] = ( ( const float * ) sums - > data ) [ j * ne0 + i ] / count ;
}
} else {
// Partial imatrix data, this tensor never got any input during calibration
for ( int64_t i = 0 ; i < ne0 ; + + i ) {
e [ j * ne0 + i ] = 1 ;
}
2024-09-06 17:17:25 -04:00
}
if ( count > max_count ) {
max_count = count ;
}
}
2024-04-03 15:07:05 +02:00
if ( getenv ( " LLAMA_TRACE " ) ) {
2024-09-10 12:09:17 -04:00
printf ( " %s: loaded data (size = %6d, n_tokens = %6d, n_chunks = %6d) for '%s' \n " , __func__ , int ( e . size ( ) ) , int ( max_count ) , int ( max_count / chunk_size ) , name . c_str ( ) ) ;
2024-01-14 09:45:56 +02:00
}
}
2024-04-26 20:06:33 +02:00
2024-09-06 17:17:25 -04:00
int m_last_chunk = gguf_get_val_u32 ( ctx_gguf , chunk_count_idx ) ;
2025-04-15 17:29:57 -04:00
int64_t n_datasets = gguf_get_arr_n ( ctx_gguf , dataset_idx ) ;
2025-06-23 12:43:25 -04:00
imatrix_datasets . reserve ( n_datasets ) ;
2025-04-15 17:29:57 -04:00
for ( int64_t i = 0 ; i < n_datasets ; + + i ) {
imatrix_datasets . push_back ( gguf_get_val_str ( ctx_gguf , dataset_idx ) ) ;
}
printf ( " %s: imatrix datasets=['%s' " , __func__ , imatrix_datasets [ 0 ] . c_str ( ) ) ;
for ( size_t i = 1 ; i < imatrix_datasets . size ( ) ; + + i ) {
printf ( " , '%s' " , imatrix_datasets [ i ] . c_str ( ) ) ;
}
printf ( " ] \n " ) ;
2024-09-06 17:17:25 -04:00
printf ( " %s: loaded %d importance matrix entries from %s computed on %d chunks \n " , __func__ , int ( imatrix_data . size ( ) ) , imatrix_file . c_str ( ) , m_last_chunk ) ;
2024-09-08 10:04:01 -04:00
gguf_free ( ctx_gguf ) ;
ggml_free ( ctx ) ;
2024-09-06 17:17:25 -04:00
return m_last_chunk ;
2024-01-14 09:45:56 +02:00
}
2024-04-26 20:06:33 +02:00
static int prepare_imatrix ( const std : : string & imatrix_file ,
2025-04-15 17:29:57 -04:00
std : : vector < std : : string > & imatrix_dataset ,
2024-03-26 13:09:30 +01:00
const std : : vector < std : : string > & included_weights ,
const std : : vector < std : : string > & excluded_weights ,
std : : unordered_map < std : : string , std : : vector < float > > & imatrix_data ) {
2024-04-26 20:06:33 +02:00
int m_last_call = - 1 ;
2024-01-14 09:45:56 +02:00
if ( ! imatrix_file . empty ( ) ) {
2024-04-26 20:06:33 +02:00
m_last_call = load_imatrix ( imatrix_file , imatrix_dataset , imatrix_data ) ;
2024-01-14 09:45:56 +02:00
}
if ( imatrix_data . empty ( ) ) {
2024-04-26 20:06:33 +02:00
return m_last_call ;
2024-01-14 09:45:56 +02:00
}
if ( ! excluded_weights . empty ( ) ) {
2025-04-15 17:29:57 -04:00
for ( const auto & name : excluded_weights ) {
for ( auto it = imatrix_data . begin ( ) ; it ! = imatrix_data . end ( ) ; ) {
2024-01-14 09:45:56 +02:00
auto pos = it - > first . find ( name ) ;
2025-04-15 17:29:57 -04:00
if ( pos ! = std : : string : : npos ) {
it = imatrix_data . erase ( it ) ;
} else {
+ + it ;
}
2024-01-14 09:45:56 +02:00
}
}
}
if ( ! included_weights . empty ( ) ) {
std : : unordered_map < std : : string , std : : vector < float > > tmp ;
2025-04-15 17:29:57 -04:00
for ( const auto & name : included_weights ) {
for ( auto & e : imatrix_data ) {
2024-01-14 09:45:56 +02:00
auto pos = e . first . find ( name ) ;
if ( pos ! = std : : string : : npos ) {
tmp . emplace ( std : : move ( e ) ) ;
}
}
}
imatrix_data = std : : move ( tmp ) ;
}
if ( ! imatrix_data . empty ( ) ) {
printf ( " %s: have %d importance matrix entries \n " , __func__ , int ( imatrix_data . size ( ) ) ) ;
}
2024-04-26 20:06:33 +02:00
return m_last_call ;
2024-01-14 09:45:56 +02:00
}
2024-03-22 19:47:14 +01:00
static ggml_type parse_ggml_type ( const char * arg ) {
2024-09-20 20:55:36 +02:00
for ( int i = 0 ; i < GGML_TYPE_COUNT ; + + i ) {
auto type = ( ggml_type ) i ;
2024-03-22 19:47:14 +01:00
const auto * name = ggml_type_name ( type ) ;
2024-09-20 20:55:36 +02:00
if ( name & & striequals ( name , arg ) ) {
return type ;
2024-03-22 19:47:14 +01:00
}
}
2025-05-13 18:12:31 +01:00
fprintf ( stderr , " \n %s: invalid ggml_type '%s' \n \n " , __func__ , arg ) ;
2024-09-20 20:55:36 +02:00
return GGML_TYPE_COUNT ;
2024-03-22 19:47:14 +01:00
}
2025-04-13 19:29:28 +01:00
static bool parse_tensor_type ( const char * data , std : : vector < tensor_quantization > & tensor_type ) {
const char * sep = strchr ( data , ' = ' ) ;
if ( sep = = nullptr ) {
printf ( " \n %s: malformed tensor type '%s' \n \n " , __func__ , data ) ;
return false ;
}
const size_t tn_len = sep - data ;
if ( tn_len = = 0 ) {
printf ( " \n %s: missing tensor name \n \n " , __func__ ) ;
return false ;
}
if ( const size_t qt_len = strlen ( sep ) ; qt_len = = 1 ) {
printf ( " \n %s: missing quantization type \n \n " , __func__ ) ;
return false ;
}
std : : string tn ( data , tn_len ) ;
std : : transform ( tn . begin ( ) , tn . end ( ) , tn . begin ( ) , tolower ) ;
sep + + ;
tensor_quantization tqz ;
tqz . name = tn ;
2025-05-13 18:12:31 +01:00
tqz . quant = parse_ggml_type ( sep ) ;
2025-04-13 19:29:28 +01:00
tensor_type . emplace_back ( std : : move ( tqz ) ) ;
2025-05-13 18:12:31 +01:00
if ( tqz . quant = = GGML_TYPE_COUNT ) {
printf ( " \n %s: invalid quantization type '%s' \n \n " , __func__ , sep ) ;
return false ;
}
2025-04-13 19:29:28 +01:00
return true ;
}
2023-03-10 20:40:58 +02:00
int main ( int argc , char * * argv ) {
2023-05-05 00:58:56 +02:00
if ( argc < 3 ) {
2023-06-10 01:59:17 -06:00
usage ( argv [ 0 ] ) ;
}
llama_model_quantize_params params = llama_model_quantize_default_params ( ) ;
int arg_idx = 1 ;
2024-01-14 09:45:56 +02:00
std : : string imatrix_file ;
std : : vector < std : : string > included_weights , excluded_weights ;
2024-03-26 13:09:30 +01:00
std : : vector < llama_model_kv_override > kv_overrides ;
2025-04-13 19:29:28 +01:00
std : : vector < tensor_quantization > tensor_types ;
2023-06-10 01:59:17 -06:00
for ( ; arg_idx < argc & & strncmp ( argv [ arg_idx ] , " -- " , 2 ) = = 0 ; arg_idx + + ) {
if ( strcmp ( argv [ arg_idx ] , " --leave-output-tensor " ) = = 0 ) {
params . quantize_output_tensor = false ;
2024-03-22 19:47:14 +01:00
} else if ( strcmp ( argv [ arg_idx ] , " --output-tensor-type " ) = = 0 ) {
if ( arg_idx < argc - 1 ) {
params . output_tensor_type = parse_ggml_type ( argv [ + + arg_idx ] ) ;
2024-09-20 20:55:36 +02:00
if ( params . output_tensor_type = = GGML_TYPE_COUNT ) {
usage ( argv [ 0 ] ) ;
}
2024-03-22 19:47:14 +01:00
} else {
usage ( argv [ 0 ] ) ;
}
} else if ( strcmp ( argv [ arg_idx ] , " --token-embedding-type " ) = = 0 ) {
if ( arg_idx < argc - 1 ) {
params . token_embedding_type = parse_ggml_type ( argv [ + + arg_idx ] ) ;
2024-09-20 20:55:36 +02:00
if ( params . token_embedding_type = = GGML_TYPE_COUNT ) {
usage ( argv [ 0 ] ) ;
}
2024-03-22 19:47:14 +01:00
} else {
usage ( argv [ 0 ] ) ;
}
2025-04-13 19:29:28 +01:00
} else if ( strcmp ( argv [ arg_idx ] , " --tensor-type " ) = = 0 ) {
if ( arg_idx = = argc - 1 | | ! parse_tensor_type ( argv [ + + arg_idx ] , tensor_types ) ) {
usage ( argv [ 0 ] ) ;
}
2024-03-26 13:09:30 +01:00
} else if ( strcmp ( argv [ arg_idx ] , " --override-kv " ) = = 0 ) {
2024-05-22 20:04:20 +03:00
if ( arg_idx = = argc - 1 | | ! string_parse_kv_override ( argv [ + + arg_idx ] , kv_overrides ) ) {
2024-03-26 13:09:30 +01:00
usage ( argv [ 0 ] ) ;
}
2023-06-10 01:59:17 -06:00
} else if ( strcmp ( argv [ arg_idx ] , " --allow-requantize " ) = = 0 ) {
params . allow_requantize = true ;
2023-10-29 18:32:28 +02:00
} else if ( strcmp ( argv [ arg_idx ] , " --pure " ) = = 0 ) {
params . pure = true ;
2024-01-14 09:45:56 +02:00
} else if ( strcmp ( argv [ arg_idx ] , " --imatrix " ) = = 0 ) {
if ( arg_idx < argc - 1 ) {
imatrix_file = argv [ + + arg_idx ] ;
} else {
usage ( argv [ 0 ] ) ;
}
} else if ( strcmp ( argv [ arg_idx ] , " --include-weights " ) = = 0 ) {
if ( arg_idx < argc - 1 ) {
2024-02-03 12:23:37 +01:00
included_weights . emplace_back ( argv [ + + arg_idx ] ) ;
2024-01-14 09:45:56 +02:00
} else {
usage ( argv [ 0 ] ) ;
}
} else if ( strcmp ( argv [ arg_idx ] , " --exclude-weights " ) = = 0 ) {
if ( arg_idx < argc - 1 ) {
2024-02-03 12:23:37 +01:00
excluded_weights . emplace_back ( argv [ + + arg_idx ] ) ;
2024-01-14 09:45:56 +02:00
} else {
usage ( argv [ 0 ] ) ;
}
2024-05-19 11:37:04 -05:00
} else if ( strcmp ( argv [ arg_idx ] , " --keep-split " ) = = 0 ) {
2024-04-25 18:29:35 +08:00
params . keep_split = true ;
2023-06-10 01:59:17 -06:00
} else {
usage ( argv [ 0 ] ) ;
2023-04-26 18:43:27 +02:00
}
2023-06-10 01:59:17 -06:00
}
2023-08-28 02:32:25 -04:00
if ( argc - arg_idx < 2 ) {
2024-01-14 09:45:56 +02:00
printf ( " %s: bad arguments \n " , argv [ 0 ] ) ;
usage ( argv [ 0 ] ) ;
}
if ( ! included_weights . empty ( ) & & ! excluded_weights . empty ( ) ) {
2023-06-10 01:59:17 -06:00
usage ( argv [ 0 ] ) ;
2023-03-10 20:40:58 +02:00
}
2025-04-15 17:29:57 -04:00
std : : vector < std : : string > imatrix_datasets ;
2024-01-14 09:45:56 +02:00
std : : unordered_map < std : : string , std : : vector < float > > imatrix_data ;
2025-04-15 17:29:57 -04:00
int m_last_call = prepare_imatrix ( imatrix_file , imatrix_datasets , included_weights , excluded_weights , imatrix_data ) ;
2024-01-14 09:45:56 +02:00
if ( ! imatrix_data . empty ( ) ) {
params . imatrix = & imatrix_data ;
2024-04-26 20:06:33 +02:00
{
llama_model_kv_override kvo ;
std : : strcpy ( kvo . key , LLM_KV_QUANTIZE_IMATRIX_FILE ) ;
kvo . tag = LLAMA_KV_OVERRIDE_TYPE_STR ;
strncpy ( kvo . val_str , imatrix_file . c_str ( ) , 127 ) ;
kvo . val_str [ 127 ] = ' \0 ' ;
kv_overrides . emplace_back ( std : : move ( kvo ) ) ;
}
2025-04-15 17:29:57 -04:00
if ( ! imatrix_datasets . empty ( ) ) {
2024-04-26 20:06:33 +02:00
llama_model_kv_override kvo ;
2025-04-15 17:29:57 -04:00
// TODO: list multiple datasets when there are more than one
2024-04-26 20:06:33 +02:00
std : : strcpy ( kvo . key , LLM_KV_QUANTIZE_IMATRIX_DATASET ) ;
kvo . tag = LLAMA_KV_OVERRIDE_TYPE_STR ;
2025-04-15 17:29:57 -04:00
strncpy ( kvo . val_str , imatrix_datasets [ 0 ] . c_str ( ) , 127 ) ;
2024-04-26 20:06:33 +02:00
kvo . val_str [ 127 ] = ' \0 ' ;
kv_overrides . emplace_back ( std : : move ( kvo ) ) ;
}
{
llama_model_kv_override kvo ;
std : : strcpy ( kvo . key , LLM_KV_QUANTIZE_IMATRIX_N_ENTRIES ) ;
kvo . tag = LLAMA_KV_OVERRIDE_TYPE_INT ;
kvo . val_i64 = imatrix_data . size ( ) ;
kv_overrides . emplace_back ( std : : move ( kvo ) ) ;
}
if ( m_last_call > 0 ) {
llama_model_kv_override kvo ;
std : : strcpy ( kvo . key , LLM_KV_QUANTIZE_IMATRIX_N_CHUNKS ) ;
kvo . tag = LLAMA_KV_OVERRIDE_TYPE_INT ;
kvo . val_i64 = m_last_call ;
kv_overrides . emplace_back ( std : : move ( kvo ) ) ;
}
2024-01-14 09:45:56 +02:00
}
2024-03-26 13:09:30 +01:00
if ( ! kv_overrides . empty ( ) ) {
kv_overrides . emplace_back ( ) ;
kv_overrides . back ( ) . key [ 0 ] = 0 ;
params . kv_overrides = & kv_overrides ;
}
2025-04-13 19:29:28 +01:00
if ( ! tensor_types . empty ( ) ) {
params . tensor_types = & tensor_types ;
}
2024-01-14 09:45:56 +02:00
2024-02-16 01:31:07 -08:00
llama_backend_init ( ) ;
2023-03-11 17:40:14 +02:00
2023-05-05 00:58:56 +02:00
// parse command line arguments
2023-06-10 01:59:17 -06:00
const std : : string fname_inp = argv [ arg_idx ] ;
arg_idx + + ;
2023-05-05 00:58:56 +02:00
std : : string fname_out ;
std : : string ftype_str ;
2024-04-25 18:29:35 +08:00
std : : string suffix = " .gguf " ;
2023-06-10 01:59:17 -06:00
if ( try_parse_ftype ( argv [ arg_idx ] , params . ftype , ftype_str ) ) {
2023-05-05 00:58:56 +02:00
std : : string fpath ;
2023-08-28 02:32:25 -04:00
const size_t pos = fname_inp . find_last_of ( " / \\ " ) ;
2023-05-05 00:58:56 +02:00
if ( pos ! = std : : string : : npos ) {
fpath = fname_inp . substr ( 0 , pos + 1 ) ;
}
2024-04-25 18:29:35 +08:00
// export as [inp path]/ggml-model-[ftype]. Only add extension if there is no splitting
fname_out = fpath + " ggml-model- " + ftype_str ;
if ( ! params . keep_split ) {
fname_out + = suffix ;
}
2023-05-05 00:58:56 +02:00
arg_idx + + ;
2023-09-01 08:02:48 -06:00
if ( ftype_str = = " COPY " ) {
params . only_copy = true ;
}
2024-03-26 13:09:30 +01:00
} else {
2023-05-05 00:58:56 +02:00
fname_out = argv [ arg_idx ] ;
2024-04-25 18:29:35 +08:00
if ( params . keep_split & & fname_out . find ( suffix ) ! = std : : string : : npos ) {
fname_out = fname_out . substr ( 0 , fname_out . length ( ) - suffix . length ( ) ) ;
}
2023-05-05 00:58:56 +02:00
arg_idx + + ;
2023-03-10 20:40:58 +02:00
2023-05-05 00:58:56 +02:00
if ( argc < = arg_idx ) {
fprintf ( stderr , " %s: missing ftype \n " , __func__ ) ;
return 1 ;
}
2023-06-10 01:59:17 -06:00
if ( ! try_parse_ftype ( argv [ arg_idx ] , params . ftype , ftype_str ) ) {
2023-05-05 00:58:56 +02:00
fprintf ( stderr , " %s: invalid ftype '%s' \n " , __func__ , argv [ 3 ] ) ;
return 1 ;
2023-09-07 13:22:29 -04:00
}
if ( ftype_str = = " COPY " ) {
params . only_copy = true ;
2023-05-05 00:58:56 +02:00
}
arg_idx + + ;
}
// parse nthreads
if ( argc > arg_idx ) {
try {
2023-06-10 01:59:17 -06:00
params . nthread = std : : stoi ( argv [ arg_idx ] ) ;
2023-05-05 00:58:56 +02:00
}
catch ( const std : : exception & e ) {
fprintf ( stderr , " %s: invalid nthread '%s' (%s) \n " , __func__ , argv [ arg_idx ] , e . what ( ) ) ;
2023-04-26 18:43:27 +02:00
return 1 ;
}
}
2024-02-18 18:16:55 +02:00
if ( ( params . ftype = = LLAMA_FTYPE_MOSTLY_IQ2_XS | | params . ftype = = LLAMA_FTYPE_MOSTLY_IQ2_XXS | |
2024-02-26 18:28:38 +02:00
params . ftype = = LLAMA_FTYPE_MOSTLY_IQ2_S | |
2024-03-26 15:21:27 +01:00
params . ftype = = LLAMA_FTYPE_MOSTLY_Q2_K_S | |
params . ftype = = LLAMA_FTYPE_MOSTLY_IQ1_S | |
params . ftype = = LLAMA_FTYPE_MOSTLY_IQ1_M ) & & imatrix_data . empty ( ) ) {
fprintf ( stderr , " \n ========================================================================================================== \n " ) ;
fprintf ( stderr , " Please do not use IQ1_S, IQ1_M, IQ2_S, IQ2_XXS, IQ2_XS or Q2_K_S quantization without an importance matrix \n " ) ;
fprintf ( stderr , " ========================================================================================================== \n \n \n " ) ;
2024-01-14 09:45:56 +02:00
return 1 ;
}
2023-09-15 16:59:49 -04:00
print_build_info ( ) ;
2023-05-01 09:23:47 -07:00
2023-05-05 00:58:56 +02:00
fprintf ( stderr , " %s: quantizing '%s' to '%s' as %s " , __func__ , fname_inp . c_str ( ) , fname_out . c_str ( ) , ftype_str . c_str ( ) ) ;
2023-06-10 01:59:17 -06:00
if ( params . nthread > 0 ) {
fprintf ( stderr , " using %d threads " , params . nthread ) ;
2023-05-05 00:58:56 +02:00
}
fprintf ( stderr , " \n " ) ;
2023-03-10 20:40:58 +02:00
2023-05-20 11:06:11 +03:00
const int64_t t_main_start_us = llama_time_us ( ) ;
2023-03-10 20:40:58 +02:00
int64_t t_quantize_us = 0 ;
// load the model
{
2023-05-20 11:06:11 +03:00
const int64_t t_start_us = llama_time_us ( ) ;
2023-03-10 20:40:58 +02:00
2023-06-10 01:59:17 -06:00
if ( llama_model_quantize ( fname_inp . c_str ( ) , fname_out . c_str ( ) , & params ) ) {
2023-03-10 20:40:58 +02:00
fprintf ( stderr , " %s: failed to quantize model from '%s' \n " , __func__ , fname_inp . c_str ( ) ) ;
return 1 ;
}
2023-05-20 11:06:11 +03:00
t_quantize_us = llama_time_us ( ) - t_start_us ;
2023-03-10 20:40:58 +02:00
}
// report timing
{
2023-05-20 11:06:11 +03:00
const int64_t t_main_end_us = llama_time_us ( ) ;
2023-03-10 20:40:58 +02:00
printf ( " \n " ) ;
2023-03-28 16:48:20 +00:00
printf ( " %s: quantize time = %8.2f ms \n " , __func__ , t_quantize_us / 1000.0 ) ;
printf ( " %s: total time = %8.2f ms \n " , __func__ , ( t_main_end_us - t_main_start_us ) / 1000.0 ) ;
2023-03-10 20:40:58 +02:00
}
2023-07-10 11:49:56 -04:00
llama_backend_free ( ) ;
2023-03-10 20:40:58 +02:00
return 0 ;
}