当前位置: 首页 > news >正文

模型加载与初始化(3)

前言

在 llama.cpp 中,模型推理主要基于 GGUF 格式展开。GGUF 是一种专为存储基于 GGML 及其相关执行器进行推理的模型文件而设计的格式。作为一种二进制格式,其设计初衷在于实现模型的高效加载与保存,并确保良好的易读性。本章将深入探讨大语言模型推理的关键准备环节——模型的加载与初始化部分,并从 GGUF格式介绍及GGUF格式加载与解析两个维度展开详细介绍。


目录

  • 1 GGUF格式介绍
  • 2 GGUF格式加载与解析

1 GGUF格式介绍

GGUF具有单文件部署、高度可扩展、兼容mmap、简单易用、信息自包含等特点。即模型无需额外文件即可单独部署,通过元数据键值对实现功能的平滑扩展,配合 mmap 显著提升了加载与写入效率凭借无需外部依赖库的特性简化了读写流程,并因内置所有必要信息而省去了用户的额外配置。GGUF文件结构如下图所示(引用自ggml/docs/gguf.md at master · ggml-org/ggml):

GGUF 文件由头文件、张量信息(Tensor Info)及张量数据(Tensors)三部分构成:头文件描述GGUF文件的全局信息,包含基础文件信息(如格式标识(GGUF字符)、版本号、张量与元数据计数)及以键值对(Key-Value Pairs)形式存储的丰富元数据;张量信息部分详细描述了各权重的名称、维度、数据类型及物理偏移量,为权重数据读取提供索引;张量数据区则存储实际的权重值,并严格遵循元数据中 general.alignment 定义的对齐值,通过填充0x00确保各张量首地址满足对齐要求-每个张量的首地址需为general.alignment的整数倍,从而优化内存映射与推理性能。

2 GGUF格式加载与解析

接下来从代码层面剖析 GGUF 的解析过程。在上篇文章中(llama-server - 从命令行到HTTP Server(2)-CSDN博客),我们介绍了llama-server基于cpp-httplib的运行机制,其整体架构主要分为server_http_context 与 server_context,其中server_context负责管理模型推理,而接下来的大模型加载相关逻辑则属于server_context的核心内容。

本次调试的llama.cpp版本的hash值为7f5ee5...,可通过git reset命令与本文保持一致。

调试的命令如下,在后续文章中除特别说明外,都采用此命令:

llama-server -m gemma-3-1b-it-Q4_K_M.gguf -ngl 99

回到 llama-server 项目 server.cpp 文件的第 252-259 行,通过调用 server_context 类的 load_model 方法即可实现模型的加载。

if (!ctx_server.load_model(params)) { clean_up(); if (ctx_http.thread.joinable()) { ctx_http.thread.join(); } LOG_ERR("%s: exiting due to model loading error\n", __func__); return 1; }

函数传入的参数为common_params对象,其包含了大模型服务所有的参数,本文除n_gpu_layers取值99外,其它参数取默认值。

通过深入调试 load_model 方法,可以总结出如下函数调用路径:common_init_from_params -> llama_model_load_from_file。其中 common_init_from_params 函数返回 common_init_result 对象,而 common_init_result 主要包含 llama_model 与 llama_context 两类核心对象,其类依赖关系如下图所示。而 llama_model_load_from_file 函数在 common_init_result 对象的构造过程中执行,传入参数为记录了模型路径及配套参数的 common_params。


接下来,进入对 llama_model_load_from_file 函数的调试环节。从该函数的输入输出来看,其接收模型路径与额外参数并输出 llama_model 对象;初步观察可见,llama_model 对象的成员变量涵盖了超参数、词汇表、ggml_tensor、llama_layer、以 std::unordered_map 形式表达的 KV 元数据、计算后端设备及后端缓存等,其与成员变量的具体关系如下图所示。

继续向下调试,进入到 llama_model_load_from_file_impl 函数(位于 llama.cpp 中),其代码前半部分主要涉及 llama_model 指针的构造以及后端设备(device)的添加过程,而有关 llama.cpp 的后端初始化逻辑将在后续章节详细讲解。接下来调试会遇到一个关键函数 llama_model_load,进入该函数后可以发现,其在实现上是先通过 llama_model_loader 对象加载模型,再利用该对象逐步构建 llama_model 实例,因此后续的代码解读部分将分为模型加载与 llama_model 对象构建两阶段进行。

2.1模型加载

首先从结构体llama_model_loader的成员变量来分析,其主要包含一个gguf_context指针对象和ggml_context指针数组。

ggml_context与gguf_context并不一定呈现一一对应关系,这主要是由于GGUF格式支持跨多个文件拆分存储,相关信息在KV元数据的split.count字段中有所体现;在本次调试的模型中,由于该值为0,因此ggml_context与gguf_context在此时表现为一一对应关系。

继续深入调试,在llama_model_loader的构造函数中,通过调用gguf_init_from_file函数返回gguf_context对象并同步初始化ggml_context对象;观察gguf_context结构体可以发现,其设计方案与2.1节介绍的GGUF格式组成高度契合,即GGUF文件由文件头、张量信息(TensorInfo)及张量数据(Tensors)三部分构成,其中文件头对应version与kv变量,张量信息对应info,而张量数据则对应offset、size与 data;此外还特别包含对齐变量alignment,用于计算首个张量数据需满足对齐要求的地址偏移量,即offset变量值。

struct gguf_context { uint32_t version = GGUF_VERSION; std::vector<struct gguf_kv> kv; std::vector<struct gguf_tensor_info> info; size_t alignment = GGUF_DEFAULT_ALIGNMENT; size_t offset = 0; // offset of `data` from beginning of file size_t size = 0; // size of `data` in bytes void * data = nullptr; };

2.2llama_model对象构建

在模型加载完成后,断点回退至 llama_model_load函数,此时llama_model_loader对象构建完毕,并转由其构造 llama_model对象。接着,系统依次调用load_arch、load_hparams、load_vocab、load_stats及load_tensors方法,并以llama_model_loader对象作为形参实现信息交换;相关代码分布在llama.cpp文件的第844-861行。值得注意的是,load_tensors方法的调用与GPU操作紧密相关,通过对比调用前后的状态,可以观察到GPU占用显著升高,而load_tensors的具体实现细节将在后续章节深入剖析。

2.3 小结

本小节通过深入剖析代码的形式,阐述了从server_context到llama_model、再到gguf_context与ggml_context的调用路径,清晰还原了GGUF格式的加载与解析流程。

其中底层gguf_context的设计与物理存储保持高度一致,ggml_context负责张量的进一步解析,而最顶层的llama_model则涉及数据从CPU到GPU的计算后端加载过程;至于llama_context、后端初始化及load_tensors的细致实现等本节尚未涉及的知识点,将在后续章节深入讲解。

文末

本文深入讲解了大语言模型(LLM)推理中重要的准备工作之一,模型加载与初始化。从GGUF格式及其加载与解析两个角度去说明。同时在代码调试过程中,留下了llama_context、后端初始化及load_tensors的细致实现等本节尚未涉及的知识点,这些将在后续文章中深入讲解。在下一章中,我们将进一步探索模型推理前的另一核心准备环节——从文本到Token。

大语言模型无法直接处理原始字符串,需要将输入文本映射为高维向量序列,作为后续模型推理计算的输入,此过程中有一个必不可少的环节——分词(tokenize,即从文本到Token)。

http://www.jsqmd.com/news/559880/

相关文章:

  • PyTorch实战:用自编码器给MNIST数字图片瘦身(附完整代码)
  • 小米智能家居完美接入Home Assistant:3步实现全屋智能联动
  • 用AI写Python游戏代码靠谱吗?实测极狐CodeRider-Kilo生成俄罗斯方块的坑与惊喜
  • js之工作者线程
  • XML学习
  • 百川2-13B-4bits模型加速技巧:OpenClaw任务响应速度提升30%的配置优化
  • 突破百度网盘限速的5个实用技巧:免费高速下载全攻略
  • 在PC上畅玩Switch游戏:Ryujinx模拟器完全指南
  • Emby Premiere免费解锁终极指南:轻松享受高级媒体服务器功能
  • TypeScript 一日速通指南:TypeScript可以做全栈开发吗?
  • 洛雪音乐音源全解析:一站式解锁全网高品质音乐资源
  • Python PDF文本提取终极指南:3分钟掌握pdftotext的完整教程
  • 告别TeamViewer!用RustDesk自建服务器实现跨平台远程控制(Windows/Ubuntu客户端全配置)
  • Agent-S:重新定义人机协作的智能体框架技术解析
  • PP-DocLayoutV3效果实测:低光照/模糊/压缩失真文档的布局识别容错能力
  • OpenClaw安装避坑指南:macOS下对接GLM-4.7-Flash全流程
  • 2026年完全指南:OpenClaw LCM 插件 — 再也不会丢失任何对话
  • MATLAB信号处理实战:用buttord和butter函数搞定巴特沃斯滤波器设计(附完整代码)
  • 终极防撤回解决方案:RevokeMsgPatcher完全攻略
  • 家庭自动化整合:OpenClaw+nanobot控制智能家居的配置方案
  • 2026年哪款工具最稳把AI率降到20%以内?年度实测红黑榜 - 我要发一区
  • 51单片机实战:四键操控LED实现多样动态效果
  • 若依框架下,如何让JimuReport积木报表乖乖认你的登录状态?(附完整前后端代码)
  • 原神帧率解锁指南:突破60帧限制的完整方案
  • 3个核心优势:AsrTools语音转文字全流程解决方案
  • 别光看协议!用Wireshark抓包实战分析PCIe TLP的First DW BE和Last DW BE
  • SenseVoice-Small模型在运维监控中的语音告警应用
  • 如何用ESP32-S3模组实现带屏幕AI小智对话
  • Claude Code 命令行参数实践指南
  • OpenClaw性能对比:nanobot轻量模型vs标准大模型