#include "llama-quant.h"
-
#include "llama-impl.h"
#include "llama-model.h"
#include "llama-model-loader.h"
}
}
+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 {
const llama_model & model;
const llama_model_quantize_params * params;
const size_t align = GGUF_DEFAULT_ALIGNMENT;
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
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
}
}
+ std::map<int, std::string> mapped;
+ int blk_id = 0;
+ int pruned_attention_w = 0;
+
// make a list of weights
std::vector<const llama_model_loader::llama_tensor_weight *> tensors;
tensors.reserve(ml.weights_map.size());
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);
}
+ 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
if (params->keep_split) {
if (llama_model_has_encoder(&model)) {
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;
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_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());
}
}
const float * imatrix = nullptr;
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()) {
LLAMA_LOG_INFO("\n====== %s: did not find weights for %s\n", __func__, tensor->name);
} else {
/*.imatrix =*/ nullptr,
/*.kv_overrides =*/ nullptr,
/*.tensor_type =*/ nullptr,
+ /*.prune_layers =*/ nullptr
};
return result;
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]]
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(" [--token-embedding-type] [--tensor-type] [--keep-split] [--override-kv] model-f32.gguf [model-quant.gguf] type [nthreads]\n\n");
+ printf("usage: %s [--help] [--allow-requantize] [--leave-output-tensor] [--pure] [--imatrix] [--include-weights]\n", executable);
+ 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(" --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(" --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(" 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(" --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");
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) {
if (argc < 3) {
usage(argv[0]);
std::vector<std::string> included_weights, excluded_weights;
std::vector<llama_model_kv_override> kv_overrides;
std::vector<tensor_quantization> tensor_types;
+ std::vector<int> prune_layers;
for (; arg_idx < argc && strncmp(argv[arg_idx], "--", 2) == 0; arg_idx++) {
if (strcmp(argv[arg_idx], "--leave-output-tensor") == 0) {
if (arg_idx == argc-1 || !parse_tensor_type(argv[++arg_idx], tensor_types)) {
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) {
if (arg_idx == argc-1 || !string_parse_kv_override(argv[++arg_idx], kv_overrides)) {
usage(argv[0]);
if (!tensor_types.empty()) {
params.tensor_types = &tensor_types;
}
+ if (!prune_layers.empty()) {
+ params.prune_layers = &prune_layers;
+ }
llama_backend_init();