Files
ncnn/tools/quantize/ncnnllm2int.cpp
2026-07-13 10:13:39 +08:00

1016 lines
31 KiB
C++

// Copyright 2019 BUG1989
// Copyright 2026 Tencent
// SPDX-License-Identifier: BSD-3-Clause
#ifdef _MSC_VER
#define _CRT_SECURE_NO_DEPRECATE
#endif
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <string>
#include <vector>
// ncnn public header
#include "datareader.h"
#include "layer.h"
#include "layer_type.h"
#include "net.h"
// ncnn private header
#include "../modelwriter.h"
#include "ncnnllm_quant.h"
class DataReaderFromEmpty : public ncnn::DataReader
{
public:
virtual int scan(const char* /*format*/, void* /*p*/) const
{
return 0;
}
virtual size_t read(void* buf, size_t size) const
{
memset(buf, 0, size);
return size;
}
};
class LLMWeightScale
{
public:
LLMWeightScale()
{
bits = 0;
block_size = 0;
}
public:
int bits;
int block_size;
std::string qweight;
ncnn::Mat scales;
};
static bool qweight_path_is_absolute(const char* path)
{
if (path[0] == '/')
return true;
#ifdef _WIN32
if (strlen(path) >= 3 && path[1] == ':' && (path[2] == '\\' || path[2] == '/'))
return true;
#endif
return false;
}
static std::string resolve_qweight_path(const char* tablepath, const char* path)
{
if (qweight_path_is_absolute(path))
return std::string(path);
const char* slash = strrchr(tablepath, '/');
#ifdef _WIN32
const char* backslash = strrchr(tablepath, '\\');
if (backslash && (!slash || backslash > slash))
slash = backslash;
#endif
if (!slash)
return std::string(path);
return std::string(tablepath, slash - tablepath + 1) + path;
}
static inline int sign_extend(int v, int bits)
{
const int sign_bit = 1 << (bits - 1);
return (v ^ sign_bit) - sign_bit;
}
static inline int unpack_signed_weight(const unsigned char* ptr, int k, int bits, int packed_k_bytes)
{
const int bit_offset = k * bits;
const int byte_offset = bit_offset / 8;
const int bit_shift = bit_offset % 8;
unsigned int v = ptr[byte_offset];
if (byte_offset + 1 < packed_k_bytes)
v |= (unsigned int)ptr[byte_offset + 1] << 8;
const int mask = (1 << bits) - 1;
return sign_extend((v >> bit_shift) & mask, bits);
}
static bool parse_int_string(const char* s, int& v)
{
int nconsumed = 0;
if (sscanf(s, "%d%n", &v, &nconsumed) != 1 || s[nconsumed] != '\0')
return false;
return true;
}
static bool read_line(FILE* fp, std::vector<char>& line)
{
line.clear();
char buf[4096];
while (fgets(buf, sizeof(buf), fp))
{
const size_t len = strlen(buf);
line.insert(line.end(), buf, buf + len);
if (len > 0 && buf[len - 1] == '\n')
break;
}
if (line.empty())
return false;
line.push_back('\0');
return true;
}
static bool read_llm_scale_table(const char* filepath, std::map<std::string, LLMWeightScale>& llm_scale_table)
{
llm_scale_table.clear();
FILE* fp = fopen(filepath, "rb");
if (!fp)
{
fprintf(stderr, "Open %s failed.\n", filepath);
return false;
}
std::string key_str;
std::vector<float> scales;
std::vector<char> line;
char* pch = NULL;
while (read_line(fp, line))
{
line[strcspn(line.data(), "\r\n")] = 0;
pch = strtok(line.data(), " \t");
if (pch == NULL)
continue;
if (pch[0] == '#')
continue;
char key[256];
sscanf(pch, "%255s", key);
key_str = key;
LLMWeightScale scale;
scales.clear();
bool coeff_started = false;
while ((pch = strtok(NULL, " \t")) != NULL)
{
char* eqs = strchr(pch, '=');
if (eqs && !coeff_started)
{
eqs[0] = '\0';
const char* k = pch;
const char* v = eqs + 1;
if (v[0] == '\0')
{
fprintf(stderr, "%s malformed metadata %s=\n", filepath, k);
fclose(fp);
return false;
}
if (strcmp(k, "bits") == 0)
{
if (!parse_int_string(v, scale.bits))
{
fprintf(stderr, "%s malformed bits=%s\n", filepath, v);
fclose(fp);
return false;
}
}
else if (strcmp(k, "block") == 0)
{
if (!parse_int_string(v, scale.block_size))
{
fprintf(stderr, "%s malformed block=%s\n", filepath, v);
fclose(fp);
return false;
}
}
else if (strcmp(k, "qweight") == 0)
{
scale.qweight = resolve_qweight_path(filepath, v);
}
else if (strcmp(k, "method") != 0)
{
fprintf(stderr, "%s unsupported metadata %s\n", filepath, k);
fclose(fp);
return false;
}
}
else
{
if (eqs && coeff_started)
{
fprintf(stderr, "%s key=value token after coefficients started\n", filepath);
fclose(fp);
return false;
}
coeff_started = true;
float coeff = 0.f;
if (sscanf(pch, "%f", &coeff) != 1)
{
fprintf(stderr, "%s malformed coefficient %s\n", filepath, pch);
fclose(fp);
return false;
}
scales.push_back(coeff);
}
}
scale.scales = ncnn::Mat((int)scales.size(), (void*)scales.data()).clone();
llm_scale_table[key_str] = scale;
}
fclose(fp);
return true;
}
static int make_input_scaled_weight(const ncnn::Mat& weight_data, const ncnn::Mat& input_scales, ncnn::Mat& weight_data_scaled)
{
const int K = weight_data.w;
const int N = weight_data.h;
if (input_scales.w != K)
{
fprintf(stderr, "input scale count mismatch expected=%d got=%d\n", K, input_scales.w);
return -1;
}
weight_data_scaled.create(K, N);
if (weight_data_scaled.empty())
return -100;
const float* input_scale_ptr = input_scales;
for (int n = 0; n < N; n++)
{
const float* ptr = weight_data.row(n);
float* outptr = weight_data_scaled.row(n);
for (int k = 0; k < K; k++)
outptr[k] = ptr[k] / input_scale_ptr[k];
}
return 0;
}
class NetQuantize : public ModelWriter
{
public:
NetQuantize();
public:
int quantize_gemm(int block_size, int weight_bits, int method);
int quantize_gemm_from_table(std::map<std::string, LLMWeightScale>& llm_scale_table);
int quantize_multiheadattention(int block_size, int weight_bits, int method);
int quantize_multiheadattention_from_table(std::map<std::string, LLMWeightScale>& llm_scale_table);
};
NetQuantize::NetQuantize()
: ModelWriter()
{
}
static void print_usage(const char* argv0)
{
fprintf(stderr, "Usage: %s [inparam] [inbin] [outparam] [outbin] [(key=value)...]\n", argv0);
fprintf(stderr, " %s [inparam] [inbin] [outparam] [outbin] [llm.table] [(key=value)...]\n", argv0);
fprintf(stderr, " method=minmax/mseclip\n");
fprintf(stderr, " bits=4/6/8\n");
fprintf(stderr, " block=32/64/128\n");
fprintf(stderr, "Sample usage:\n");
fprintf(stderr, " %s model.param model.bin model-int4.param model-int4.bin method=mseclip bits=4 block=64\n", argv0);
fprintf(stderr, " %s model.param model.bin model-int4.param model-int4.bin model.llm.table\n", argv0);
}
static int llm_table_row_to_scales(const char* key, const LLMWeightScale& scale, int K, int N, int& weight_bits, int& block_size, ncnn::Mat& weight_data_quantize_scales)
{
if (scale.bits == 0 || scale.block_size == 0)
{
fprintf(stderr, "%s missing mandatory metadata\n", key);
return -1;
}
weight_bits = scale.bits;
block_size = scale.block_size;
if (llm_weight_block_quantize_term(weight_bits, block_size) == 0)
{
fprintf(stderr, "%s unsupported bits=%d block=%d\n", key, weight_bits, block_size);
return -1;
}
const int block_count = (K + block_size - 1) / block_size;
const size_t weight_scale_count = (size_t)N * block_count;
if ((size_t)scale.scales.w != weight_scale_count)
{
fprintf(stderr, "%s coefficient count mismatch expected=%zu got=%d\n", key, weight_scale_count, scale.scales.w);
return -1;
}
weight_data_quantize_scales.create(block_count, N);
if (weight_data_quantize_scales.empty())
return -100;
memcpy(weight_data_quantize_scales.data, scale.scales.data, weight_scale_count * sizeof(float));
const float* scale_ptr = weight_data_quantize_scales;
for (size_t i = 0; i < weight_scale_count; i++)
{
if (!(scale_ptr[i] > 0.f))
{
fprintf(stderr, "%s invalid weight scale index=%zu coeff=%f\n", key, i, scale_ptr[i]);
return -1;
}
}
return 0;
}
static int read_qweight_file(const char* key, const LLMWeightScale& scale, int K, int N, int weight_bits, ncnn::Mat& weight_data_quantized)
{
const int packed_k_bytes = llm_weight_quantize_packed_k_bytes(K, weight_bits);
const size_t qweight_bytes = (size_t)N * packed_k_bytes;
FILE* fp = fopen(scale.qweight.c_str(), "rb");
if (!fp)
{
fprintf(stderr, "%s fopen qweight %s failed\n", key, scale.qweight.c_str());
return -1;
}
fseek(fp, 0, SEEK_END);
const long file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (file_size != (long)qweight_bytes)
{
fprintf(stderr, "%s qweight byte count mismatch expected=%zu got=%ld\n", key, qweight_bytes, file_size);
fclose(fp);
return -1;
}
weight_data_quantized.create(packed_k_bytes, N, (size_t)1u);
if (weight_data_quantized.empty())
{
fclose(fp);
return -100;
}
if (fread(weight_data_quantized.data, 1, qweight_bytes, fp) != qweight_bytes)
{
fprintf(stderr, "%s fread qweight %s failed\n", key, scale.qweight.c_str());
fclose(fp);
return -1;
}
fclose(fp);
const int used_bits = (int)(((size_t)K * weight_bits) % 8);
const unsigned char tail_mask = used_bits == 0 ? 0 : (unsigned char)(0xffu << used_bits);
const int qmin = -(1 << (weight_bits - 1));
for (int n = 0; n < N; n++)
{
const unsigned char* qptr = weight_data_quantized.row<const unsigned char>(n);
if (tail_mask && (qptr[packed_k_bytes - 1] & tail_mask))
{
fprintf(stderr, "%s qweight tail padding bits are not zero row=%d\n", key, n);
return -1;
}
for (int k = 0; k < K; k++)
{
const int q = unpack_signed_weight(qptr, k, weight_bits, packed_k_bytes);
if (q == qmin)
{
fprintf(stderr, "%s qweight contains unsupported value row=%d k=%d q=%d\n", key, n, k, q);
return -1;
}
}
}
return 0;
}
static int llm_table_row_to_qweight(const char* key, const LLMWeightScale& scale, int K, int N, int& weight_bits, int& block_size, ncnn::Mat& weight_data_quantize_scales, ncnn::Mat& weight_data_quantized)
{
if (scale.qweight.empty())
{
fprintf(stderr, "%s missing mandatory metadata\n", key);
return -1;
}
int ret = llm_table_row_to_scales(key, scale, K, N, weight_bits, block_size, weight_data_quantize_scales);
if (ret != 0)
return ret;
return read_qweight_file(key, scale, K, N, weight_bits, weight_data_quantized);
}
static int llm_table_row_to_input_scales(const char* key, const LLMWeightScale& scale, int K, ncnn::Mat& input_scales)
{
if (scale.scales.w != K)
{
fprintf(stderr, "%s coefficient count mismatch expected=%d got=%d\n", key, K, scale.scales.w);
return -1;
}
const float* ptr = scale.scales;
for (int k = 0; k < K; k++)
{
if (!(ptr[k] > 0.f))
{
fprintf(stderr, "%s invalid input scale index=%d coeff=%f\n", key, k, ptr[k]);
return -1;
}
}
input_scales = scale.scales.clone();
if (input_scales.empty())
return -100;
return 0;
}
int NetQuantize::quantize_gemm(int block_size, int weight_bits, int method)
{
const int quantize_term = llm_weight_block_quantize_term(weight_bits, block_size);
if (quantize_term == 0)
{
fprintf(stderr, "unsupported bits=%d or block=%d\n", weight_bits, block_size);
return -1;
}
int quantized_count = 0;
for (size_t i = 0; i < layers.size(); i++)
{
if (layers[i]->type != "Gemm")
continue;
ncnn::Gemm* gemm = (ncnn::Gemm*)layers[i];
if (!is_supported_llm_gemm(gemm))
{
print_skip_gemm(gemm);
continue;
}
fprintf(stderr, "quantize_gemm %s\n", gemm_name(gemm));
ncnn::Mat B_data_quantized;
ncnn::Mat B_data_quantize_scales;
const int ret = quantize_weight_data(gemm->B_data, block_size, weight_bits, method, B_data_quantized, B_data_quantize_scales);
if (ret != 0)
return ret;
gemm->B_data = B_data_quantized;
gemm->B_data_quantize_scales = B_data_quantize_scales;
gemm->quantize_term = quantize_term;
quantized_count++;
}
fprintf(stderr, "quantized %d Gemm layers\n", quantized_count);
return 0;
}
int NetQuantize::quantize_gemm_from_table(std::map<std::string, LLMWeightScale>& llm_scale_table)
{
int quantized_count = 0;
for (size_t i = 0; i < layers.size(); i++)
{
if (layers[i]->type != "Gemm")
continue;
ncnn::Gemm* gemm = (ncnn::Gemm*)layers[i];
char key[256];
snprintf(key, 256, "%s_param_1", gemm_name(gemm));
char input_scale_key[256];
snprintf(input_scale_key, 256, "%s_param_1_input_scale", gemm_name(gemm));
if (!is_supported_llm_gemm(gemm))
{
std::map<std::string, LLMWeightScale>::iterator iter = llm_scale_table.find(key);
if (iter != llm_scale_table.end())
{
fprintf(stderr, "table row %s targets unsupported Gemm\n", key);
return -1;
}
print_skip_gemm(gemm);
continue;
}
std::map<std::string, LLMWeightScale>::iterator iter = llm_scale_table.find(key);
if (iter == llm_scale_table.end())
{
fprintf(stderr, "skip_gemm %s missing table row %s\n", gemm_name(gemm), key);
continue;
}
LLMWeightScale& scale = iter->second;
const int constantN = gemm->constantN;
const int constantK = gemm->constantK;
int weight_bits = 0;
int block_size = 0;
ncnn::Mat B_data_quantize_scales;
ncnn::Mat B_data_quantized;
const bool qweight_row = !scale.qweight.empty();
int ret = 0;
if (qweight_row)
ret = llm_table_row_to_qweight(key, scale, constantK, constantN, weight_bits, block_size, B_data_quantize_scales, B_data_quantized);
else
ret = llm_table_row_to_scales(key, scale, constantK, constantN, weight_bits, block_size, B_data_quantize_scales);
if (ret != 0)
return ret;
std::map<std::string, LLMWeightScale>::iterator input_scale_iter = llm_scale_table.find(input_scale_key);
const bool has_input_scale = input_scale_iter != llm_scale_table.end();
if (qweight_row && has_input_scale)
{
fprintf(stderr, "%s does not support input_scale with qweight yet\n", key);
return -1;
}
ncnn::Mat B_data_input_scales;
if (has_input_scale)
{
ret = llm_table_row_to_input_scales(input_scale_key, input_scale_iter->second, constantK, B_data_input_scales);
if (ret != 0)
return ret;
}
const int quantize_term = llm_weight_block_quantize_term(weight_bits, block_size, has_input_scale);
fprintf(stderr, "quantize_gemm %s\n", gemm_name(gemm));
if (!qweight_row)
{
if (has_input_scale)
{
ncnn::Mat B_data_scaled;
ret = make_input_scaled_weight(gemm->B_data, B_data_input_scales, B_data_scaled);
if (ret != 0)
return ret;
ret = pack_weight_data(B_data_scaled, B_data_quantize_scales, block_size, weight_bits, B_data_quantized);
}
else
{
ret = pack_weight_data(gemm->B_data, B_data_quantize_scales, block_size, weight_bits, B_data_quantized);
}
if (ret != 0)
return ret;
}
gemm->B_data = B_data_quantized;
gemm->B_data_quantize_scales = B_data_quantize_scales;
gemm->B_data_input_scales = B_data_input_scales;
gemm->quantize_term = quantize_term;
if (has_input_scale)
llm_scale_table.erase(input_scale_iter);
llm_scale_table.erase(iter);
quantized_count++;
}
fprintf(stderr, "quantized %d Gemm layers\n", quantized_count);
return quantized_count;
}
int NetQuantize::quantize_multiheadattention(int block_size, int weight_bits, int method)
{
const int quantize_term = llm_weight_block_quantize_term(weight_bits, block_size);
if (quantize_term == 0)
{
fprintf(stderr, "unsupported bits=%d or block=%d\n", weight_bits, block_size);
return -1;
}
int quantized_count = 0;
for (size_t i = 0; i < layers.size(); i++)
{
if (layers[i]->type != "MultiHeadAttention")
continue;
ncnn::MultiHeadAttention* mha = (ncnn::MultiHeadAttention*)layers[i];
if (!is_supported_llm_multiheadattention(mha))
{
print_skip_multiheadattention(mha);
continue;
}
fprintf(stderr, "quantize_multiheadattention %s\n", multiheadattention_name(mha));
const int qdim = mha->weight_data_size / mha->embed_dim;
const ncnn::Mat q_weight_data = mha->q_weight_data.reshape(qdim, mha->embed_dim);
const ncnn::Mat k_weight_data = mha->k_weight_data.reshape(mha->kdim, mha->embed_dim);
const ncnn::Mat v_weight_data = mha->v_weight_data.reshape(mha->vdim, mha->embed_dim);
const ncnn::Mat out_weight_data = mha->out_weight_data.reshape(mha->embed_dim, qdim);
ncnn::Mat q_weight_data_quantized;
ncnn::Mat k_weight_data_quantized;
ncnn::Mat v_weight_data_quantized;
ncnn::Mat out_weight_data_quantized;
ncnn::Mat q_weight_data_quantize_scales;
ncnn::Mat k_weight_data_quantize_scales;
ncnn::Mat v_weight_data_quantize_scales;
ncnn::Mat out_weight_data_quantize_scales;
int ret = quantize_weight_data(q_weight_data, block_size, weight_bits, method, q_weight_data_quantized, q_weight_data_quantize_scales);
if (ret != 0)
return ret;
ret = quantize_weight_data(k_weight_data, block_size, weight_bits, method, k_weight_data_quantized, k_weight_data_quantize_scales);
if (ret != 0)
return ret;
ret = quantize_weight_data(v_weight_data, block_size, weight_bits, method, v_weight_data_quantized, v_weight_data_quantize_scales);
if (ret != 0)
return ret;
ret = quantize_weight_data(out_weight_data, block_size, weight_bits, method, out_weight_data_quantized, out_weight_data_quantize_scales);
if (ret != 0)
return ret;
mha->q_weight_data = q_weight_data_quantized;
mha->k_weight_data = k_weight_data_quantized;
mha->v_weight_data = v_weight_data_quantized;
mha->out_weight_data = out_weight_data_quantized;
mha->q_weight_data_quantize_scales = q_weight_data_quantize_scales;
mha->k_weight_data_quantize_scales = k_weight_data_quantize_scales;
mha->v_weight_data_quantize_scales = v_weight_data_quantize_scales;
mha->out_weight_data_quantize_scales = out_weight_data_quantize_scales;
mha->quantize_term = quantize_term;
quantized_count++;
}
fprintf(stderr, "quantized %d MultiHeadAttention layers\n", quantized_count);
return 0;
}
int NetQuantize::quantize_multiheadattention_from_table(std::map<std::string, LLMWeightScale>& llm_scale_table)
{
int quantized_count = 0;
for (size_t i = 0; i < layers.size(); i++)
{
if (layers[i]->type != "MultiHeadAttention")
continue;
ncnn::MultiHeadAttention* mha = (ncnn::MultiHeadAttention*)layers[i];
char keys[4][256];
char input_scale_keys[4][256];
for (int j = 0; j < 4; j++)
{
snprintf(keys[j], 256, "%s_param_%d", multiheadattention_name(mha), j);
snprintf(input_scale_keys[j], 256, "%s_param_%d_input_scale", multiheadattention_name(mha), j);
}
std::map<std::string, LLMWeightScale>::iterator iters[4];
int present_count = 0;
for (int j = 0; j < 4; j++)
{
iters[j] = llm_scale_table.find(keys[j]);
if (iters[j] != llm_scale_table.end())
present_count++;
}
if (present_count == 0)
continue;
if (present_count != 4)
{
fprintf(stderr, "MultiHeadAttention %s requires all table rows %s %s %s %s\n", multiheadattention_name(mha), keys[0], keys[1], keys[2], keys[3]);
return -1;
}
if (!is_supported_llm_multiheadattention(mha))
{
fprintf(stderr, "table rows target unsupported MultiHeadAttention %s\n", multiheadattention_name(mha));
return -1;
}
const int qdim = mha->weight_data_size / mha->embed_dim;
const int Ks[4] = {qdim, mha->kdim, mha->vdim, mha->embed_dim};
const int Ns[4] = {mha->embed_dim, mha->embed_dim, mha->embed_dim, qdim};
std::map<std::string, LLMWeightScale>::iterator input_scale_iters[4];
int input_scale_present_count = 0;
for (int j = 0; j < 4; j++)
{
input_scale_iters[j] = llm_scale_table.find(input_scale_keys[j]);
if (input_scale_iters[j] != llm_scale_table.end())
input_scale_present_count++;
}
if (input_scale_present_count != 0 && input_scale_present_count != 4)
{
fprintf(stderr, "MultiHeadAttention %s requires all input scale table rows %s %s %s %s\n", multiheadattention_name(mha), input_scale_keys[0], input_scale_keys[1], input_scale_keys[2], input_scale_keys[3]);
return -1;
}
int weight_bits[4];
int block_size[4];
bool qweight_rows[4];
ncnn::Mat weight_data_quantize_scales[4];
ncnn::Mat weight_data_quantized[4];
for (int j = 0; j < 4; j++)
{
qweight_rows[j] = !iters[j]->second.qweight.empty();
int ret = 0;
if (qweight_rows[j])
ret = llm_table_row_to_qweight(keys[j], iters[j]->second, Ks[j], Ns[j], weight_bits[j], block_size[j], weight_data_quantize_scales[j], weight_data_quantized[j]);
else
ret = llm_table_row_to_scales(keys[j], iters[j]->second, Ks[j], Ns[j], weight_bits[j], block_size[j], weight_data_quantize_scales[j]);
if (ret != 0)
return ret;
}
for (int j = 1; j < 4; j++)
{
if (weight_bits[j] != weight_bits[0] || block_size[j] != block_size[0])
{
fprintf(stderr, "MultiHeadAttention %s table rows require same bits/block for q/k/v/out\n", multiheadattention_name(mha));
return -1;
}
if (qweight_rows[j] != qweight_rows[0])
{
fprintf(stderr, "MultiHeadAttention %s table rows require same format for q/k/v/out\n", multiheadattention_name(mha));
return -1;
}
}
ncnn::Mat input_scales[4];
const bool has_input_scale = input_scale_present_count == 4;
if (has_input_scale && qweight_rows[0])
{
fprintf(stderr, "MultiHeadAttention %s does not support input_scale with qweight yet\n", multiheadattention_name(mha));
return -1;
}
if (has_input_scale)
{
for (int j = 0; j < 4; j++)
{
int ret = llm_table_row_to_input_scales(input_scale_keys[j], input_scale_iters[j]->second, Ks[j], input_scales[j]);
if (ret != 0)
return ret;
}
}
const int quantize_term = llm_weight_block_quantize_term(weight_bits[0], block_size[0], has_input_scale);
fprintf(stderr, "quantize_multiheadattention %s\n", multiheadattention_name(mha));
const ncnn::Mat q_weight_data = mha->q_weight_data.reshape(qdim, mha->embed_dim);
const ncnn::Mat k_weight_data = mha->k_weight_data.reshape(mha->kdim, mha->embed_dim);
const ncnn::Mat v_weight_data = mha->v_weight_data.reshape(mha->vdim, mha->embed_dim);
const ncnn::Mat out_weight_data = mha->out_weight_data.reshape(mha->embed_dim, qdim);
const ncnn::Mat weights[4] = {q_weight_data, k_weight_data, v_weight_data, out_weight_data};
for (int j = 0; j < 4; j++)
{
if (qweight_rows[j])
continue;
int ret = 0;
if (has_input_scale)
{
ncnn::Mat weight_data_scaled;
ret = make_input_scaled_weight(weights[j], input_scales[j], weight_data_scaled);
if (ret != 0)
return ret;
ret = pack_weight_data(weight_data_scaled, weight_data_quantize_scales[j], block_size[0], weight_bits[0], weight_data_quantized[j]);
}
else
{
ret = pack_weight_data(weights[j], weight_data_quantize_scales[j], block_size[0], weight_bits[0], weight_data_quantized[j]);
}
if (ret != 0)
return ret;
}
mha->q_weight_data = weight_data_quantized[0];
mha->k_weight_data = weight_data_quantized[1];
mha->v_weight_data = weight_data_quantized[2];
mha->out_weight_data = weight_data_quantized[3];
mha->q_weight_data_quantize_scales = weight_data_quantize_scales[0];
mha->k_weight_data_quantize_scales = weight_data_quantize_scales[1];
mha->v_weight_data_quantize_scales = weight_data_quantize_scales[2];
mha->out_weight_data_quantize_scales = weight_data_quantize_scales[3];
mha->q_weight_data_input_scales = input_scales[0];
mha->k_weight_data_input_scales = input_scales[1];
mha->v_weight_data_input_scales = input_scales[2];
mha->out_weight_data_input_scales = input_scales[3];
mha->quantize_term = quantize_term;
for (int j = 0; j < 4; j++)
{
if (has_input_scale)
llm_scale_table.erase(input_scale_iters[j]);
llm_scale_table.erase(iters[j]);
}
quantized_count++;
}
fprintf(stderr, "quantized %d MultiHeadAttention layers\n", quantized_count);
return quantized_count;
}
int main(int argc, char** argv)
{
if (argc < 5)
{
print_usage(argv[0]);
return -1;
}
for (int i = 1; i < argc; i++)
{
if (argv[i][0] == '-')
{
print_usage(argv[0]);
return -1;
}
}
const char* inparam = argv[1];
const char* inbin = argv[2];
const char* outparam = argv[3];
const char* outbin = argv[4];
const char* tablepath = 0;
int kv_start = 5;
if (argc >= 6 && strchr(argv[5], '=') == NULL)
{
tablepath = argv[5];
kv_start = 6;
}
int weight_bits = 6;
int block_size = 64;
const char* method = "minmax";
for (int i = kv_start; i < argc; i++)
{
// key=value
char* kv = argv[i];
char* eqs = strchr(kv, '=');
if (eqs == NULL)
{
fprintf(stderr, "unrecognized arg %s\n", kv);
return -1;
}
eqs[0] = '\0';
const char* key = kv;
const char* value = eqs + 1;
if (strcmp(key, "method") == 0)
method = value;
else if (strcmp(key, "bits") == 0)
{
if (!parse_int_string(value, weight_bits))
{
fprintf(stderr, "malformed bits=%s\n", value);
return -1;
}
}
else if (strcmp(key, "block") == 0)
{
if (!parse_int_string(value, block_size))
{
fprintf(stderr, "malformed block=%s\n", value);
return -1;
}
}
else
{
fprintf(stderr, "unrecognized arg %s\n", key);
return -1;
}
}
int quantize_method = LLM_QUANT_METHOD_MINMAX;
if (!tablepath)
{
quantize_method = llm_quant_method_from_string(method);
if (quantize_method < 0)
{
fprintf(stderr, "unsupported method=%s\n", method);
return -1;
}
if (quantize_method == LLM_QUANT_METHOD_AWQ || quantize_method == LLM_QUANT_METHOD_GPTQ)
{
fprintf(stderr, "method=%s requires llm.table\n", method);
return -1;
}
if (llm_weight_block_quantize_term(weight_bits, block_size) == 0)
{
print_usage(argv[0]);
return -1;
}
}
std::map<std::string, LLMWeightScale> llm_scale_table;
if (tablepath)
{
if (!read_llm_scale_table(tablepath, llm_scale_table))
return -1;
}
NetQuantize quantizer;
quantizer.storage_type = 1; // use fp16 where weight quant not applied
if (quantizer.load_param(inparam) != 0)
return -1;
if (strcmp(inbin, "null") == 0)
{
DataReaderFromEmpty dr;
if (quantizer.load_model(dr) != 0)
return -1;
}
else
{
if (quantizer.load_model(inbin) != 0)
return -1;
}
if (tablepath)
{
int quantized_count = quantizer.quantize_gemm_from_table(llm_scale_table);
if (quantized_count < 0)
return -1;
int quantized_count_mha = quantizer.quantize_multiheadattention_from_table(llm_scale_table);
if (quantized_count_mha < 0)
return -1;
quantized_count += quantized_count_mha;
if (!llm_scale_table.empty())
{
for (std::map<std::string, LLMWeightScale>::const_iterator it = llm_scale_table.begin(); it != llm_scale_table.end(); ++it)
fprintf(stderr, "unused table row %s\n", it->first.c_str());
return -1;
}
if (quantized_count == 0)
{
fprintf(stderr, "no layer quantized\n");
return -1;
}
}
else if (quantizer.quantize_gemm(block_size, weight_bits, quantize_method) != 0)
{
return -1;
}
else if (quantizer.quantize_multiheadattention(block_size, weight_bits, quantize_method) != 0)
{
return -1;
}
if (quantizer.save(outparam, outbin) != 0)
return -1;
return 0;
}