mirror of
				https://github.com/ggml-org/llama.cpp.git
				synced 2025-10-31 08:51:55 +00:00 
			
		
		
		
	quantize : handle user-defined pruning of whole layers (blocks) (#13037)
This commit is contained in:
		| @@ -390,6 +390,7 @@ extern "C" { | |||||||
|         void * imatrix;                       // pointer to importance matrix data |         void * imatrix;                       // pointer to importance matrix data | ||||||
|         void * kv_overrides;                  // pointer to vector containing overrides |         void * kv_overrides;                  // pointer to vector containing overrides | ||||||
|         void * tensor_types;                  // pointer to vector containing tensor types |         void * tensor_types;                  // pointer to vector containing tensor types | ||||||
|  |         void * prune_layers;                  // pointer to vector containing layer indices to prune | ||||||
|     } llama_model_quantize_params; |     } llama_model_quantize_params; | ||||||
|  |  | ||||||
|     typedef struct llama_logit_bias { |     typedef struct llama_logit_bias { | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| #include "llama-quant.h" | #include "llama-quant.h" | ||||||
|  |  | ||||||
| #include "llama-impl.h" | #include "llama-impl.h" | ||||||
| #include "llama-model.h" | #include "llama-model.h" | ||||||
| #include "llama-model-loader.h" | #include "llama-model-loader.h" | ||||||
| @@ -27,6 +26,56 @@ static void zeros(std::ofstream & file, size_t n) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static std::string remap_layer(const std::string & orig_name, const std::vector<int> & prune, std::map<int, std::string> & mapped, int & next_id) { | ||||||
|  |     if (prune.empty()) { | ||||||
|  |         return orig_name; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static const std::regex pattern(R"(blk\.(\d+)\.)"); | ||||||
|  |     if (std::smatch match; std::regex_search(orig_name, match, pattern)) { | ||||||
|  |         const int blk = std::stoi(match[1]); | ||||||
|  |         std::string new_name = orig_name; | ||||||
|  |  | ||||||
|  |         if (mapped.count(blk)) { | ||||||
|  |             // Already mapped, do nothing | ||||||
|  |         } else if (std::find(prune.begin(), prune.end(), blk) != prune.end()) { | ||||||
|  |             mapped[blk] = ""; | ||||||
|  |         } else if (blk < prune.front()) { | ||||||
|  |             mapped[blk] = std::to_string(blk); | ||||||
|  |             next_id = blk + 1; | ||||||
|  |         } else { | ||||||
|  |             mapped[blk] = std::to_string(next_id); | ||||||
|  |             ++next_id; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return mapped[blk].empty() ? mapped[blk] : new_name.replace(match.position(1), match.length(1), mapped[blk]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return orig_name; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static std::string remap_imatrix (const std::string & orig_name, const std::map<int, std::string> & mapped) { | ||||||
|  |     if (mapped.empty()) { | ||||||
|  |         return orig_name; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static const std::regex pattern(R"(blk\.(\d+)\.)"); | ||||||
|  |     if (std::smatch match; std::regex_search(orig_name, match, pattern)) { | ||||||
|  |         const std::string blk(match[1]); | ||||||
|  |         std::string new_name = orig_name; | ||||||
|  |  | ||||||
|  |         for (const auto & p : mapped) { | ||||||
|  |             if (p.second == blk) { | ||||||
|  |                 LLAMA_LOG_DEBUG("(blk.%d imatrix) ", p.first); | ||||||
|  |                 return new_name.replace(match.position(1), match.length(1), std::to_string(p.first)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         GGML_ABORT("\n%s: imatrix mapping error for %s\n", __func__, orig_name.c_str()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return orig_name; | ||||||
|  | } | ||||||
|  |  | ||||||
| struct quantize_state_impl { | struct quantize_state_impl { | ||||||
|     const llama_model                 & model; |     const llama_model                 & model; | ||||||
|     const llama_model_quantize_params * params; |     const llama_model_quantize_params * params; | ||||||
| @@ -568,6 +617,11 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std:: | |||||||
|     const size_t align = GGUF_DEFAULT_ALIGNMENT; |     const size_t align = GGUF_DEFAULT_ALIGNMENT; | ||||||
|     gguf_context_ptr ctx_out { gguf_init_empty() }; |     gguf_context_ptr ctx_out { gguf_init_empty() }; | ||||||
|  |  | ||||||
|  |     std::vector<int> prune_list = {}; | ||||||
|  |     if (params->prune_layers) { | ||||||
|  |         prune_list = *static_cast<const std::vector<int> *>(params->prune_layers); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // copy the KV pairs from the input file |     // copy the KV pairs from the input file | ||||||
|     gguf_set_kv     (ctx_out.get(), ml.meta.get()); |     gguf_set_kv     (ctx_out.get(), ml.meta.get()); | ||||||
|     gguf_set_val_u32(ctx_out.get(), "general.quantization_version", GGML_QNT_VERSION); // TODO: use LLM_KV |     gguf_set_val_u32(ctx_out.get(), "general.quantization_version", GGML_QNT_VERSION); // TODO: use LLM_KV | ||||||
| @@ -597,12 +651,32 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std:: | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     std::map<int, std::string> mapped; | ||||||
|  |     int blk_id = 0; | ||||||
|  |     int pruned_attention_w = 0; | ||||||
|  |  | ||||||
|     // make a list of weights |     // make a list of weights | ||||||
|     std::vector<const llama_model_loader::llama_tensor_weight *> tensors; |     std::vector<const llama_model_loader::llama_tensor_weight *> tensors; | ||||||
|     tensors.reserve(ml.weights_map.size()); |     tensors.reserve(ml.weights_map.size()); | ||||||
|     for (const auto & it : ml.weights_map) { |     for (const auto & it : ml.weights_map) { | ||||||
|  |         const std::string remapped_name(remap_layer(it.first, prune_list, mapped, blk_id)); | ||||||
|  |         if (remapped_name.empty()) { | ||||||
|  |             if (it.first.find("attn_v.weight") != std::string::npos || | ||||||
|  |                 it.first.find("attn_qkv.weight") != std::string::npos || | ||||||
|  |                 it.first.find("attn_kv_b.weight") != std::string::npos) { | ||||||
|  |                     pruned_attention_w++; | ||||||
|  |             } | ||||||
|  |             LLAMA_LOG_DEBUG("%s: pruning tensor %s\n", __func__, it.first.c_str()); | ||||||
|  |             continue; | ||||||
|  |         } else if (remapped_name != it.first) { | ||||||
|  |             ggml_set_name(it.second.tensor, remapped_name.c_str()); | ||||||
|  |             LLAMA_LOG_DEBUG("%s: tensor %s remapped to %s\n", __func__, it.first.c_str(), ggml_get_name(it.second.tensor)); | ||||||
|  |         } | ||||||
|         tensors.push_back(&it.second); |         tensors.push_back(&it.second); | ||||||
|     } |     } | ||||||
|  |     if (!prune_list.empty()) { | ||||||
|  |         gguf_set_val_u32(ctx_out.get(), ml.llm_kv(LLM_KV_BLOCK_COUNT).c_str(), blk_id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // keep_split requires that the weights are sorted by split index |     // keep_split requires that the weights are sorted by split index | ||||||
|     if (params->keep_split) { |     if (params->keep_split) { | ||||||
| @@ -640,7 +714,7 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std:: | |||||||
|         if (llama_model_has_encoder(&model)) { |         if (llama_model_has_encoder(&model)) { | ||||||
|             n_attn_layer *= 3; |             n_attn_layer *= 3; | ||||||
|         } |         } | ||||||
|         GGML_ASSERT((qs.n_attention_wv == n_attn_layer) && "n_attention_wv is unexpected"); |         GGML_ASSERT((qs.n_attention_wv == n_attn_layer - pruned_attention_w) && "n_attention_wv is unexpected"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     size_t total_size_org = 0; |     size_t total_size_org = 0; | ||||||
| @@ -681,7 +755,7 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std:: | |||||||
|         for (size_t i = 0; i < ctx_outs.size(); ++i) { |         for (size_t i = 0; i < ctx_outs.size(); ++i) { | ||||||
|             gguf_set_val_u16(ctx_outs[i].get(), ml.llm_kv(LLM_KV_SPLIT_NO).c_str(), i); |             gguf_set_val_u16(ctx_outs[i].get(), ml.llm_kv(LLM_KV_SPLIT_NO).c_str(), i); | ||||||
|             gguf_set_val_u16(ctx_outs[i].get(), ml.llm_kv(LLM_KV_SPLIT_COUNT).c_str(), n_split); |             gguf_set_val_u16(ctx_outs[i].get(), ml.llm_kv(LLM_KV_SPLIT_COUNT).c_str(), n_split); | ||||||
|             gguf_set_val_i32(ctx_outs[i].get(), ml.llm_kv(LLM_KV_SPLIT_TENSORS_COUNT).c_str(), ml.n_tensors); |             gguf_set_val_i32(ctx_outs[i].get(), ml.llm_kv(LLM_KV_SPLIT_TENSORS_COUNT).c_str(), (int32_t)tensors.size()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -832,7 +906,7 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std:: | |||||||
|  |  | ||||||
|             const float * imatrix = nullptr; |             const float * imatrix = nullptr; | ||||||
|             if (imatrix_data) { |             if (imatrix_data) { | ||||||
|                 auto it = imatrix_data->find(tensor->name); |                 auto it = imatrix_data->find(remap_imatrix(tensor->name, mapped)); | ||||||
|                 if (it == imatrix_data->end()) { |                 if (it == imatrix_data->end()) { | ||||||
|                     LLAMA_LOG_INFO("\n====== %s: did not find weights for %s\n", __func__, tensor->name); |                     LLAMA_LOG_INFO("\n====== %s: did not find weights for %s\n", __func__, tensor->name); | ||||||
|                 } else { |                 } else { | ||||||
| @@ -947,6 +1021,7 @@ llama_model_quantize_params llama_model_quantize_default_params() { | |||||||
|         /*.imatrix                     =*/ nullptr, |         /*.imatrix                     =*/ nullptr, | ||||||
|         /*.kv_overrides                =*/ nullptr, |         /*.kv_overrides                =*/ nullptr, | ||||||
|         /*.tensor_type                 =*/ nullptr, |         /*.tensor_type                 =*/ nullptr, | ||||||
|  |         /*.prune_layers                =*/ nullptr | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     return result; |     return result; | ||||||
|   | |||||||
| @@ -107,13 +107,11 @@ static bool try_parse_ftype(const std::string & ftype_str_in, llama_ftype & ftyp | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| // usage: |  | ||||||
| //  ./llama-quantize [--allow-requantize] [--leave-output-tensor] [--pure] models/llama/ggml-model.gguf [models/llama/ggml-model-quant.gguf] type [nthreads] |  | ||||||
| // |  | ||||||
| [[noreturn]] | [[noreturn]] | ||||||
| static void usage(const char * executable) { | static void usage(const char * executable) { | ||||||
|     printf("usage: %s [--help] [--allow-requantize] [--leave-output-tensor] [--pure] [--imatrix] [--include-weights] [--exclude-weights] [--output-tensor-type]\n", executable); |     printf("usage: %s [--help] [--allow-requantize] [--leave-output-tensor] [--pure] [--imatrix] [--include-weights]\n", executable); | ||||||
|     printf("       [--token-embedding-type] [--tensor-type] [--keep-split] [--override-kv] model-f32.gguf [model-quant.gguf] type [nthreads]\n\n"); |     printf("       [--exclude-weights] [--output-tensor-type] [--token-embedding-type] [--tensor-type] [--prune-layers] [--keep-split] [--override-kv]\n"); | ||||||
|  |     printf("       model-f32.gguf [model-quant.gguf] type [nthreads]\n\n"); | ||||||
|     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("  --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"); |     printf("  --leave-output-tensor: Will leave output.weight un(re)quantized. Increases model size but may also increase quality, especially when requantizing\n"); | ||||||
|     printf("  --pure: Disable k-quant mixtures and quantize all tensors to the same type\n"); |     printf("  --pure: Disable k-quant mixtures and quantize all tensors to the same type\n"); | ||||||
| @@ -124,6 +122,8 @@ static void usage(const char * executable) { | |||||||
|     printf("  --token-embedding-type ggml_type: use this ggml_type for the token embeddings tensor\n"); |     printf("  --token-embedding-type ggml_type: use this ggml_type for the token embeddings tensor\n"); | ||||||
|     printf("  --tensor-type TENSOR=TYPE: quantize this tensor to this ggml_type. example: --tensor-type attn_q=q8_0\n"); |     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"); |     printf("      Advanced option to selectively quantize tensors. May be specified multiple times.\n"); | ||||||
|  |     printf("  --prune-layers L0,L1,L2...comma-separated list of layer numbers to prune from the model\n"); | ||||||
|  |     printf("      Advanced option to remove all tensors from the given layers\n"); | ||||||
|     printf("  --keep-split: will generate quantized model in the same shards as input\n"); |     printf("  --keep-split: will generate quantized model in the same shards as input\n"); | ||||||
|     printf("  --override-kv KEY=TYPE:VALUE\n"); |     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"); |     printf("      Advanced option to override model metadata by key in the quantized model. May be specified multiple times.\n"); | ||||||
| @@ -286,6 +286,32 @@ static bool parse_tensor_type(const char * data, std::vector<tensor_quantization | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static bool parse_layer_prune(const char * data, std::vector<int> & prune_layers) { | ||||||
|  |     if (!data) { | ||||||
|  |         printf("\n%s: no layer pruning ids provided\n\n", __func__); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const auto block_ids = string_split<std::string>(data, ','); | ||||||
|  |     for (const auto & block_id : block_ids) { | ||||||
|  |         int id; | ||||||
|  |         try { | ||||||
|  |             id = std::stoi(block_id); | ||||||
|  |         } catch (...) { | ||||||
|  |             id = -1; | ||||||
|  |         } | ||||||
|  |         if (id < 0) { | ||||||
|  |             printf("\n%s: invalid layer id '%s'\n\n", __func__, block_id.c_str()); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         prune_layers.emplace_back(id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     sort(prune_layers.begin(), prune_layers.end()); | ||||||
|  |     prune_layers.erase(std::unique(prune_layers.begin(), prune_layers.end()), prune_layers.end()); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
| int main(int argc, char ** argv) { | int main(int argc, char ** argv) { | ||||||
|     if (argc < 3) { |     if (argc < 3) { | ||||||
|         usage(argv[0]); |         usage(argv[0]); | ||||||
| @@ -298,6 +324,7 @@ int main(int argc, char ** argv) { | |||||||
|     std::vector<std::string> included_weights, excluded_weights; |     std::vector<std::string> included_weights, excluded_weights; | ||||||
|     std::vector<llama_model_kv_override> kv_overrides; |     std::vector<llama_model_kv_override> kv_overrides; | ||||||
|     std::vector<tensor_quantization> tensor_types; |     std::vector<tensor_quantization> tensor_types; | ||||||
|  |     std::vector<int> prune_layers; | ||||||
|  |  | ||||||
|     for (; arg_idx < argc && strncmp(argv[arg_idx], "--", 2) == 0; arg_idx++) { |     for (; arg_idx < argc && strncmp(argv[arg_idx], "--", 2) == 0; arg_idx++) { | ||||||
|         if (strcmp(argv[arg_idx], "--leave-output-tensor") == 0) { |         if (strcmp(argv[arg_idx], "--leave-output-tensor") == 0) { | ||||||
| @@ -324,6 +351,10 @@ int main(int argc, char ** argv) { | |||||||
|             if (arg_idx == argc-1 || !parse_tensor_type(argv[++arg_idx], tensor_types)) { |             if (arg_idx == argc-1 || !parse_tensor_type(argv[++arg_idx], tensor_types)) { | ||||||
|                 usage(argv[0]); |                 usage(argv[0]); | ||||||
|             } |             } | ||||||
|  |         } else if (strcmp(argv[arg_idx], "--prune-layers") == 0) { | ||||||
|  |             if (arg_idx == argc-1 || !parse_layer_prune(argv[++arg_idx], prune_layers)) { | ||||||
|  |                 usage(argv[0]); | ||||||
|  |             } | ||||||
|         } else if (strcmp(argv[arg_idx], "--override-kv") == 0) { |         } else if (strcmp(argv[arg_idx], "--override-kv") == 0) { | ||||||
|             if (arg_idx == argc-1 || !string_parse_kv_override(argv[++arg_idx], kv_overrides)) { |             if (arg_idx == argc-1 || !string_parse_kv_override(argv[++arg_idx], kv_overrides)) { | ||||||
|                 usage(argv[0]); |                 usage(argv[0]); | ||||||
| @@ -411,6 +442,9 @@ int main(int argc, char ** argv) { | |||||||
|     if (!tensor_types.empty()) { |     if (!tensor_types.empty()) { | ||||||
|         params.tensor_types = &tensor_types; |         params.tensor_types = &tensor_types; | ||||||
|     } |     } | ||||||
|  |     if (!prune_layers.empty()) { | ||||||
|  |         params.prune_layers = &prune_layers; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     llama_backend_init(); |     llama_backend_init(); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Ed Addario
					Ed Addario