switch (ftype) { case LLAMA_FTYPE_ALL_F32: return"all F32"; case LLAMA_FTYPE_MOSTLY_F16: return"F16"; case LLAMA_FTYPE_MOSTLY_BF16: return"BF16"; case LLAMA_FTYPE_MOSTLY_Q4_0: return"Q4_0"; case LLAMA_FTYPE_MOSTLY_Q4_1: return"Q4_1"; case LLAMA_FTYPE_MOSTLY_Q5_0: return"Q5_0"; case LLAMA_FTYPE_MOSTLY_Q5_1: return"Q5_1"; case LLAMA_FTYPE_MOSTLY_Q8_0: return"Q8_0"; case LLAMA_FTYPE_MOSTLY_MXFP4_MOE: return"MXFP4 MoE"; case LLAMA_FTYPE_MOSTLY_Q2_K: return"Q2_K - Medium"; case LLAMA_FTYPE_MOSTLY_Q2_K_S: return"Q2_K - Small"; case LLAMA_FTYPE_MOSTLY_Q3_K_S: return"Q3_K - Small"; case LLAMA_FTYPE_MOSTLY_Q3_K_M: return"Q3_K - Medium"; case LLAMA_FTYPE_MOSTLY_Q3_K_L: return"Q3_K - Large"; case LLAMA_FTYPE_MOSTLY_Q4_K_S: return"Q4_K - Small"; case LLAMA_FTYPE_MOSTLY_Q4_K_M: return"Q4_K - Medium"; case LLAMA_FTYPE_MOSTLY_Q5_K_S: return"Q5_K - Small"; case LLAMA_FTYPE_MOSTLY_Q5_K_M: return"Q5_K - Medium"; case LLAMA_FTYPE_MOSTLY_Q6_K: return"Q6_K"; case LLAMA_FTYPE_MOSTLY_TQ1_0: return"TQ1_0 - 1.69 bpw ternary"; case LLAMA_FTYPE_MOSTLY_TQ2_0: return"TQ2_0 - 2.06 bpw ternary"; case LLAMA_FTYPE_MOSTLY_IQ2_XXS: return"IQ2_XXS - 2.0625 bpw"; case LLAMA_FTYPE_MOSTLY_IQ2_XS: return"IQ2_XS - 2.3125 bpw"; 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_IQ3_XXS: return"IQ3_XXS - 3.0625 bpw"; case LLAMA_FTYPE_MOSTLY_IQ1_S: return"IQ1_S - 1.5625 bpw"; case LLAMA_FTYPE_MOSTLY_IQ1_M: return"IQ1_M - 1.75 bpw"; case LLAMA_FTYPE_MOSTLY_IQ4_NL: return"IQ4_NL - 4.5 bpw"; case LLAMA_FTYPE_MOSTLY_IQ4_XS: return"IQ4_XS - 4.25 bpw"; case LLAMA_FTYPE_MOSTLY_IQ3_S: return"IQ3_S - 3.4375 bpw"; case LLAMA_FTYPE_MOSTLY_IQ3_M: return"IQ3_S mix - 3.66 bpw";
default: return"unknown, may not work";
}
}
// return a list of splits for a given path // for example, given "<name>-00002-of-00004.gguf", returns list of all 4 splits static std::vector<std::string> llama_get_list_splits(const std::string & path, constint idx, constint n_split) {
std::vector<std::string> paths;
std::string split_prefix;
std::vector<char> buf(llama_path_max(), 0);
{ int ret = llama_split_prefix(buf.data(), buf.size(), path.c_str(), idx, n_split); if (!ret) { throw std::runtime_error(format("invalid split file name: %s", path.c_str()));
}
split_prefix = std::string(buf.data(), ret);
}
if (split_prefix.empty()) { throw std::runtime_error(format("invalid split file: %s", path.c_str()));
}
for (int idx = 0; idx < n_split; ++idx) { int ret = llama_split_path(buf.data(), buf.size(), split_prefix.c_str(), idx, n_split);
paths.push_back(std::string(buf.data(), ret));
}
if (kt != GKV::gt) { throw std::runtime_error(format("key %s has wrong type %s but expected type %s",
gguf_get_key(ctx, k), gguf_type_name(kt), gguf_type_name(GKV::gt)));
} return GKV::getter(ctx, k);
}
staticconstchar * override_type_to_str(const llama_model_kv_override_type ty) { switch (ty) { case LLAMA_KV_OVERRIDE_TYPE_BOOL: return"bool"; case LLAMA_KV_OVERRIDE_TYPE_INT: return"int"; case LLAMA_KV_OVERRIDE_TYPE_FLOAT: return"float"; case LLAMA_KV_OVERRIDE_TYPE_STR: return"str";
} return"unknown";
}
staticbool validate_override(const llama_model_kv_override_type expected_type, conststruct llama_model_kv_override * ovrd) { if (!ovrd) { returnfalse; } if (ovrd->tag == expected_type) {
LLAMA_LOG_INFO("%s: Using metadata override (%5s) '%s' = ",
__func__, override_type_to_str(ovrd->tag), ovrd->key); switch (ovrd->tag) { case LLAMA_KV_OVERRIDE_TYPE_BOOL: {
LLAMA_LOG_INFO("%s\n", ovrd->val_bool ? "true" : "false");
} break; case LLAMA_KV_OVERRIDE_TYPE_INT: {
LLAMA_LOG_INFO("%" PRId64 "\n", ovrd->val_i64);
} break; case LLAMA_KV_OVERRIDE_TYPE_FLOAT: {
LLAMA_LOG_INFO("%.6f\n", ovrd->val_f64);
} break; case LLAMA_KV_OVERRIDE_TYPE_STR: {
LLAMA_LOG_INFO("%s\n", ovrd->val_str);
} break; default: // Shouldn't be possible to end up here, but just in case... throw std::runtime_error(
format("Unsupported attempt to override %s type for metadata key %s\n",
override_type_to_str(ovrd->tag), ovrd->key));
} returntrue;
}
LLAMA_LOG_WARN("%s: Warning: Bad metadata override type for key '%s', expected %s but got %s\n",
__func__, ovrd->key, override_type_to_str(expected_type), override_type_to_str(ovrd->tag)); returnfalse;
}
if (kid < 0 || gguf_get_kv_type(ctx, kid) != GGUF_TYPE_ARRAY) { if (required) { throw std::runtime_error(format("array key not found in model: %s", key.c_str()));
} returnfalse;
}
switch (arr_info.gt) { case GGUF_TYPE_UINT32: case GGUF_TYPE_INT32: GGML_ASSERT((std::is_same<T, int32_t>::value) ||
(std::is_same<T, uint32_t>::value)); break; case GGUF_TYPE_FLOAT32: GGML_ASSERT((std::is_same<T, float>::value)); break; case GGUF_TYPE_STRING: GGML_ASSERT((std::is_same<T, std::string>::value)); break; default: throw std::runtime_error(format("%s is not a string/float32/uint32/int32 array", key.c_str()));
}
if (kid < 0 || gguf_get_kv_type(ctx, kid) != GGUF_TYPE_ARRAY) { if (required) { throw std::runtime_error(format("array key not found in model: %s", key.c_str()));
} returnfalse;
}
switch (arr_info.gt) { case GGUF_TYPE_UINT32: case GGUF_TYPE_INT32: GGML_ASSERT((std::is_same<T, int32_t>::value) ||
(std::is_same<T, uint32_t>::value)); break; case GGUF_TYPE_FLOAT32: GGML_ASSERT((std::is_same<T, float>::value)); break; case GGUF_TYPE_STRING: GGML_ASSERT((std::is_same<T, std::string>::value)); break; default: throw std::runtime_error(format("%s is not a string/float32/uint32/int32 array", key.c_str()));
}
if (arr_info.length > N_MAX) { throw std::runtime_error(format("array length %u for key %s exceeds max %u", (uint32_t) arr_info.length, key.c_str(), (uint32_t) N_MAX));
}
template<> bool llama_model_loader::get_key(enum llm_kv kid, enum llama_pooling_type & result, bool required) {
uint32_t tmp; constbool found = get_key(kid, tmp, required); if (found) {
result = (enum llama_pooling_type) tmp;
} else {
result = LLAMA_POOLING_TYPE_UNSPECIFIED;
} return found;
}
// get array of n <= N_MAX elements, or a single element repeated n times template<typename T, size_t N_MAX> bool llama_model_loader::get_key_or_arr(const std::string & key, std::array<T, N_MAX> & result, uint32_t n, bool required) { constint kid = gguf_find_key(meta.get(), key.c_str());
if (kid < 0) { if (required) { throw std::runtime_error(format("key not found in model: %s", key.c_str()));
} returnfalse;
}
if (n > N_MAX) { throw std::runtime_error(format("n > N_MAX: %u > %u for key %s", (uint32_t) n, (uint32_t) N_MAX, key.c_str()));
}
meta.reset(gguf_init_from_file(fname.c_str(), params)); if (!meta) { throw std::runtime_error(format("%s: failed to load model from %s", __func__, fname.c_str()));
}
// Save tensors data offset of the main file. // For subsidiary files, `meta` tensor data offset must not be used, // so we build a unified tensors index for weights. for (ggml_tensor * cur = ggml_get_first_tensor(ctx); cur; cur = ggml_get_next_tensor(ctx, cur)) {
std::string tensor_name = std::string(cur->name); // make sure there is no duplicated tensor names if (weights_map.find(tensor_name) != weights_map.end()) { throw std::runtime_error(format("invalid model: tensor '%s' is duplicated", ggml_get_name(cur)));
}
n_elements += ggml_nelements(cur);
n_bytes += ggml_nbytes(cur);
weights_map.emplace(tensor_name, llama_tensor_weight(files.back().get(), 0, meta.get(), cur));
}
uint16_t n_split = 0;
get_key(llm_kv(LLM_KV_SPLIT_COUNT), n_split, false);
// Load additional GGML contexts if (n_split > 1) { // make sure the main file is loaded first
uint16_t idx = 0; const std::string kv_split_no = llm_kv(LLM_KV_SPLIT_NO);
get_key(kv_split_no, idx); if (idx != 0) { throw std::runtime_error(format("illegal split file idx: %d (file: %s), model must be loaded with the first split", idx, fname.c_str()));
}
// generate list of splits if needed if (splits.empty()) {
splits = llama_get_list_splits(fname, idx, n_split);
}
// in case user give a custom list of splits, check if it matches the expected number if (n_split != (uint16_t)splits.size()) { throw std::runtime_error(format("invalid split count, given: %zu splits, but expected %d", splits.size(), n_split));
}
// Save tensors data offset info of the shard. for (ggml_tensor * cur = ggml_get_first_tensor(ctx); cur; cur = ggml_get_next_tensor(ctx, cur)) {
std::string tensor_name = std::string(cur->name); // make sure there is no duplicated tensor names if (weights_map.find(tensor_name) != weights_map.end()) { throw std::runtime_error(format("invalid model: tensor '%s' is duplicated", ggml_get_name(cur)));
}
n_elements += ggml_nelements(cur);
n_bytes += ggml_nbytes(cur);
weights_map.emplace(tensor_name, llama_tensor_weight(files.back().get(), idx, ctx_gguf.get(), cur));
}
}
LLAMA_LOG_INFO("%s: loaded meta data with %d key-value pairs and %d tensors from %s (version %s)\n",
__func__, n_kv, n_tensors, fname.c_str(), llama_file_version_name(fver));
// determine file type based on the number of tensors for each quantization and print meta data // TODO: make optional
{
std::map<enum ggml_type, uint32_t> n_type;
meta.reset(gguf_init_from_buffer(buffer, buffer_size, params)); if (!meta) { throw std::runtime_error(format("%s: failed to load model from buffer", __func__));
}
LLAMA_LOG_INFO("%s: loaded meta data with %d key-value pairs and %d tensors from buffer (%zu MB)\n",
__func__, n_kv, n_tensors, buffer_size / (1024 * 1024));
// Buffer-based loading uses no mmap and stores tensors in buffer this->use_mmap = false; this->check_tensors = check_tensors;
}
meta.reset(gguf_init_from_file_handle(file, params)); if (!meta) { throw std::runtime_error(format("%s: failed to load model from file handle", __func__));
}
// Build tensors index for weights // Since we're using a file handle directly, we won't populate the files vector // Instead, we'll handle file I/O through the file_handle member for (ggml_tensor * cur = ggml_get_first_tensor(ctx); cur; cur = ggml_get_next_tensor(ctx, cur)) {
std::string tensor_name = std::string(cur->name); // make sure there are no duplicated tensor names if (weights_map.find(tensor_name) != weights_map.end()) { throw std::runtime_error(format("invalid model: tensor '%s' is duplicated", ggml_get_name(cur)));
}
n_elements += ggml_nelements(cur);
n_bytes += ggml_nbytes(cur);
weights_map.emplace(tensor_name, llama_tensor_weight(file_size, 0, meta.get(), cur));
}
// File handle-based loading doesn't support splits - set defaults
ftype = LLAMA_FTYPE_GUESSED;
fver = GGUF_FILE_VERSION_V3;
LLAMA_LOG_INFO("%s: loaded meta data with %d key-value pairs and %d tensors from file handle (%zu MB)\n",
__func__, n_kv, n_tensors, file_size / (1024 * 1024));
auto * dev = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_CPU); if (dev) { auto * reg = ggml_backend_dev_backend_reg(dev); auto * is_numa_fn = (decltype(ggml_is_numa) *) ggml_backend_reg_get_proc_address(reg, "ggml_backend_cpu_is_numa"); if (is_numa_fn) {
is_numa = is_numa_fn();
}
}
// 4 staging buffers for async uploads, each sized 1MB seems to be a good default for single NVMe drives. // NVMe raid configurations might require more / larger buffers.
constexpr size_t n_buffers = 4;
constexpr size_t buffer_size = 1 * 1024 * 1024; // 1MB
std::vector<ggml_backend_buffer_t> host_buffers;
std::vector<ggml_backend_event_t> events;
std::vector<void *> host_ptrs;
size_t buffer_idx = 0; // buffer to use for async loads
ggml_backend_t upload_backend = [&](constchar * func) -> ggml_backend_t { if (use_mmap || check_tensors) { return nullptr;
} // When not using mmaped io use async uploads from pinned memory to GPU memory. // First determine if the backend supports the necessary features for async uploads. auto * buf = bufs.count(0) ? bufs.at(0) : nullptr; if (!buf) {
LLAMA_LOG_DEBUG("%s: no buffer found for async uploads\n", func); return nullptr;
}
auto * buft = ggml_backend_buffer_get_type(buf); auto * dev = ggml_backend_buft_get_device(buft); if (!dev) {
LLAMA_LOG_DEBUG("%s: no device found for buffer type %s for async uploads\n", func,
ggml_backend_buft_name(buft)); return nullptr;
}
if (buft != ggml_backend_dev_buffer_type(dev)) {
LLAMA_LOG_DEBUG("%s: buffer type %s is not the default buffer type for device %s for async uploads\n", func,
ggml_backend_buft_name(buft), ggml_backend_dev_name(dev)); return nullptr;
}
ggml_backend_dev_props props;
ggml_backend_dev_get_props(dev, &props); if (!props.caps.async || !props.caps.host_buffer || !props.caps.events) {
LLAMA_LOG_DEBUG("%s: device %s does not support async, host buffers or events\n", func,
ggml_backend_dev_name(dev)); return nullptr;
}
auto * host_buft = ggml_backend_dev_host_buffer_type(dev); if (!host_buft) {
LLAMA_LOG_DEBUG("%s: no host buffer type found for device %s\n", func,
ggml_backend_dev_name(dev)); return nullptr;
}
// If the backend is supported, create pinned memory buffers and events for synchronisation. for (size_t idx = 0; idx < n_buffers; ++idx) { auto * buf = ggml_backend_buft_alloc_buffer(host_buft, buffer_size); if (!buf) {
LLAMA_LOG_DEBUG("%s: failed to allocate host buffer for async uploads for device %s\n", func,
ggml_backend_dev_name(dev)); return nullptr;
}
auto * event = ggml_backend_event_new(dev); if (!event) {
LLAMA_LOG_DEBUG("%s: failed to create event for async uploads for device %s\n", func,
ggml_backend_dev_name(dev)); return nullptr;
}
events.emplace_back(event);
}
ggml_backend_t backend = ggml_backend_dev_init(dev, nullptr); if (!backend) {
LLAMA_LOG_DEBUG("%s: failed to initialize backend for device %s for async uploads\n", func,
ggml_backend_dev_name(dev)); return nullptr;
}
return backend;
}(__func__);
if (upload_backend) {
LLAMA_LOG_DEBUG("%s: using async uploads for device %s, buffer type %s, backend %s\n", __func__,
ggml_backend_dev_name(ggml_backend_get_device(upload_backend)),
ggml_backend_buft_name(ggml_backend_buffer_get_type(bufs.at(0))),
ggml_backend_name(upload_backend));
}
for (struct ggml_tensor * cur = ggml_get_first_tensor(ctx); cur != NULL; cur = ggml_get_next_tensor(ctx, cur)) { constauto * weight = get_weight(ggml_get_name(cur)); if (weight == nullptr) { // this can happen with split experts models continue;
}
if (progress_callback) { if (!progress_callback((float) size_done / size_data, progress_callback_user_data)) { returnfalse;
}
}
size_t n_size = ggml_nbytes(cur);
if (use_mmap) { constauto & mapping = mappings.at(weight->idx);
ggml_backend_buffer_t buf_mmap = nullptr; if (bufs.count(weight->idx)) {
buf_mmap = bufs.at(weight->idx);
}
uint8_t * data = (uint8_t *) mapping->addr() + weight->offs;
if (check_tensors) {
validation_result.push_back(std::make_pair(cur, ggml_validate_row_data(cur->type, data, n_size)));
}
GGML_ASSERT(buf_mmap || cur->data); // either we have a buffer to allocate the tensor in, or it is already allocated if (buf_mmap && cur->data == nullptr) {
ggml_backend_tensor_alloc(buf_mmap, cur, data); if (lmlocks) { constauto & lmlock = lmlocks->at(weight->idx);
lmlock->grow_to(weight->offs + n_size);
}
// free temporary resources used for async uploads for (auto * event : events) {
ggml_backend_event_synchronize(event);
ggml_backend_event_free(event);
} for (auto * buf : host_buffers) {
ggml_backend_buffer_free(buf);
}
ggml_backend_free(upload_backend);
// check validation results bool validation_failed = false; for (constauto & result : validation_result) { if (!result.second) {
LLAMA_LOG_ERROR("%s: tensor '%s' has invalid data\n", __func__, ggml_get_name(result.first));
validation_failed = true;
}
} if (validation_failed) { throw std::runtime_error("found tensors with invalid data");
}
// check if this is the last call and do final cleanup if (size_done >= size_data) { // unmap offloaded tensors and metadata if (use_mmap) { for (uint32_t idx = 0; idx < mappings.size(); idx++) { constauto & mmap_used = mmaps_used.at(idx); auto & mapping = mappings.at(idx);
mapping->unmap_fragment(0, mmap_used.first); if (mmap_used.second != 0) {
mapping->unmap_fragment(mmap_used.second, mapping->size());
}
}
} if (progress_callback) { // Even though the model is done loading, we still honor // cancellation since we need to free allocations. return progress_callback(1.0f, progress_callback_user_data);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.