textgen 五大模型加载器能力边界全解:LoRA、多模态与困惑度评估的 Loader 兼容性指南
本文以 textgen 仓库的 [What Works 能力矩阵](https://gitcode.com/GitHub_Trending/te/textgen/blob/79b46b80ec7ec98141c570dbc26f867fdfc39ead/docs/What Works.md?utm_source=gitcode_repo_files) 为主体,逐项拆解 llama.cpp、Transformers、ExLlamav3_HF、ExLlamav3、TensorRT-LLM 五个加载器在 LoRA 加载、LoRA 训练、多模态视觉和困惑度评估四项关键能力上的支持情况。读完本文,你将能结合源码级的实现证据,为自己的硬件与使用场景(微调、看图、模型质量对比)选出正确的工作流,并理解每种能力在 textgen 内部的真实调用路径。
一、能力总览:原文档的 What Works 矩阵
[What Works.md](https://gitcode.com/GitHub_Trending/te/textgen/blob/79b46b80ec7ec98141c570dbc26f867fdfc39ead/docs/What Works.md?utm_source=gitcode_repo_files) 给出的是五个加载器在四项能力上的支持情况:
| Loader | Loading LoRAs | Training LoRAs | Multimodal | Perplexity evaluation |
|---|---|---|---|---|
| llama.cpp | ❌ | ❌ | ✅* | ❌ |
| Transformers | ✅ | ✅ | ✅** | ✅ |
| ExLlamav3_HF | ❌ | ❌ | ❌ | ✅ |
| ExLlamav3 | ❌ | ❌ | ✅ | ❌ |
| TensorRT-LLM | ❌ | ❌ | ❌ | ❌ |
其中:❌ = 不支持,✅ = 支持;* 表示 llama.cpp 的多模态通过 mmproj 参数(多模态投影器文件)实现;** 表示 Transformers 的多模态通过 send_pictures 扩展实现。
这张矩阵看似简单,但直接决定了三个高频决策:
- 要不要用这个 Loader 挂 LoRA? 只有 Transformers 可以;
- 要不要用它做 LoRA 微调? 同样只有 Transformers;
- 需要看图(Vision)? llama.cpp(mmproj)、Transformers(send_pictures 扩展)、ExLlamav3 三个可以;
- 要对比模型/量化版本的困惑度? 只能用 Transformers 和 ExLlamav3_HF。
下文逐列拆解每个"✅"和"❌"背后的源码依据。
二、五个加载器在 textgen 中的定位
先确认矩阵中五个名字在代码里的真实身份。模型加载入口 load_model 通过一张加载器分发表将请求路由到具体实现:
load_func_map = {
'llama.cpp': llama_cpp_server_loader,
'Transformers': transformers_loader,
'ExLlamav3_HF': ExLlamav3_HF_loader,
'ExLlamav3': ExLlamav3_loader,
'TensorRT-LLM': TensorRT_LLM_loader,
}
每个加载器还各自持有一组专属命令行/UI 参数,集中定义在 modules/loaders.py 的 loaders_and_params 中,例如:
llama.cpp:gpu_layers、ctx_size、split_mode、mmproj(多模态投影器)等 30+ 项;Transformers:gpu_split、quant_type、load_in_4bit/8bit、attn_implementation等;ExLlamav3_HF/ExLlamav3:ctx_size、gpu_split、投机解码(draft model)与张量并行(enable_tp)相关项;TensorRT-LLM:ctx_size、tensorrt_llm_info。
这些参数决定了各加载器背后的推理引擎形态:llama.cpp 走独立的 LlamaServer 子进程,Transformers 直接持有 Hugging Face 模型对象,ExLlamav3 家族与 TensorRT-LLM 则使用各自的加速内核。理解这一层后,下面四个能力列的差异就都能对上了。
三、Loading LoRAs:为什么只有 Transformers 打勾
3.1 实现证据:PEFT 绑定在 HF 模型上
textgen 的 LoRA 热插拔逻辑全部位于 modules/LoRA.py,核心函数 add_lora_transformers 完全基于 Hugging Face 生态的 peft 库:
def add_lora_transformers(lora_names):
from peft import PeftModel
...
shared.model = PeftModel.from_pretrained(
shared.model, get_lora_path(lora_names[0]),
adapter_name=lora_names[0], **params)
for lora in lora_names[1:]:
shared.model.load_adapter(get_lora_path(lora), lora)
这里能成立的前提是 shared.model 是一个可被 PeftModel.from_pretrained 包装的 HF 模型——只有 Transformers 加载器(以及同为 HF 模型对象的 ExLlamav3_HF)满足这一形态。llama.cpp 的模型在独立子进程里,textgen 主进程拿不到其内部权重,无法外挂 adapter;ExLlamav3 / TensorRT-LLM 的模型对象也不是 PEFT 可操作的 HF 结构。这与矩阵中"仅 Transformers ✅"一致;从源码结构看,add_lora_to_model 实际上就是 add_lora_transformers 的别名(modules/LoRA.py),进一步印证 LoRA 路径是 Transformers 专属工作流。
3.2 几个实现细节
- LoRA 路径解析:get_lora_path 将 LoRA 名解析为
{shared.args.lora_dir}/{lora_name},即默认位于user_data/loras/; - 增量热切换:函数先计算
added_set/removed_set差集,无变化时直接返回;已有 LoRA 且再挂新 LoRA 时走load_adapter增量路径,移除任一 LoRA 时则shared.model.unload()重新加载基座模型再应用(modules/LoRA.py); - 多 LoRA 合并:merge_loras 用
add_weighted_adapter把多个 adapter 以等权([1] * n)合成__merged并设为激活 adapter;如果各 LoRA 的秩r不一致,会打日志警告"只有第一个生效"。这是使用多 LoRA 时的实际限制; - 量化场景的 dtype 处理:非 4bit/8bit 加载时传入模型
dtype与hf_device_map前缀映射(base_model.model.),8bit/CPU 场景则跳过.half()迁移,避免破坏量化状态。
3.3 界面入口
LoRA 选择菜单在 Model Tab 中:ui_model_menu.py 提供多选下拉框(choices 来自 utils.get_available_loras(),即扫描 user_data/loras/),点击"Apply LoRAs"按钮触发 load_lora_wrapper 调用上面的 add_lora_to_model。注意该菜单是全局的——文档矩阵的"❌"意味着:若当前以 llama.cpp 等加载器加载模型,挂 LoRA 没有意义,应切回 Transformers 加载器操作。
四、Training LoRAs:同样只有 Transformers
矩阵中"Training LoRAs"一列与"Loading LoRAs"完全同型。textgen 的 LoRA 微调实现在 modules/training.py,其依赖链(PEFT 的 LoraConfig/get_peft_model、HF Trainer、datasets)全部建立在 HF 模型对象之上,因此只能作用于 Transformers 加载器加载的模型;llama.cpp 子进程模型、ExLlamav3 内核模型与 TensorRT-LLM 引擎都不暴露可供 PEFT 注入的 HF 结构,故为 ❌。
实操含义:
- 想在 textgen 的 Training Tab 里做 LoRA 微调 → 用 Transformers 加载器加载基座模型;
- 微调产物(保存在
user_data/training/相关目录)挂回推理时 → 仍然走 Transformers 加载器 + 第三节的 Apply LoRAs 流程; - llama.cpp 用户若要用自训 LoRA,从本仓库能力矩阵看,textgen 侧不支持热加载,属于加载器层面的能力缺口。
五、Multimodal:三条技术路线对应三个 ✅
矩阵中多模态列有三个 ✅,但注释揭示了它们是三条完全不同的技术路线。
5.1 llama.cpp:mmproj 参数(*)
命令行定义在 modules/shared.py:
group.add_argument('--mmproj', type=str, default=None,
help='Path to the mmproj file for vision models.')
启动时 LlamaServer 会解析 --mmproj 路径(支持相对 user_data/mmproj/ 或相对 model_dir 的文件名),并把 --mmproj 拼进 llama.cpp 服务子进程的启动命令,同时 is_mmproj 判定 决定该会话是否启用视觉能力。文件发现逻辑在 modules/utils.py:get_available_mmproj() 扫描 user_data/mmproj/ 目录以及主模型目录中的 mmproj-*.gguf / *.bin 文件,结果注入 Model Tab 的 "Multimodal (vision)" 折叠面板下拉框(ui_model_menu.py)。此外 models_settings.py 提供"同级目录自动探测":当 loader 为 llama.cpp 且该模型未保存过 mmproj 设置时,若模型目录中恰好只有一个 mmproj 文件,会自动填入,免去手动选择。
5.2 Transformers:send_pictures 扩展(**)
Transformers 路线走扩展机制:extensions/send_pictures/script.py 提供聊天框的图片发送与预处理能力,由 HF 模型自身的视觉编码部分完成理解。这与仓库文档 [多模态使用教程](https://gitcode.com/GitHub_Trending/te/textgen/blob/79b46b80ec7ec98141c570dbc26f867fdfc39ead/docs/Multimodal Tutorial.md?utm_source=gitcode_repo_files) 的叙述一致:HF 模型若本身是视觉语言模型(如 LLaVA 系、Qwen-VL 系等),配合该扩展即可在 Chat Tab 中"喂图"。
5.3 ExLlamav3:引擎内建支持
矩阵中 ExLlamav3(非 _HF 版)✅ 表示该引擎自身支持多模态推理;而 ExLlamav3_HF 为 ❌,TensorRT-LLM 为 ❌。结合 5.1 的 mmproj 机制可以推断:ExLlamav3 的视觉支持面向的是其引擎自身可解析的投影器格式,与 llama.cpp 的 gguf mmproj 是相互独立的路径。选型时注意区分 ExLlamav3 与 ExLlamav3_HF 两个加载器——名字相近,但多模态与参数集都不同(对比 modules/loaders.py 中两者各自的参数列表即可看出)。
六、Perplexity evaluation:Transformers 与 ExLlamav3_HF 的公共底座
6.1 实现:基于 HF 模型的滑动窗口 NLL
困惑度计算全部实现在 modules/evaluate.py 的 calculate_perplexity。方法学上参考了 Hugging Face Transformers 的 fixed-length PPL 文档(代码注释中说明了来源),核心循环:
for begin_loc in tqdm(range(0, seq_len, stride)):
end_loc = min(begin_loc + max_length, seq_len)
input_ids = encodings[:, begin_loc:end_loc]
target_ids = input_ids.clone()
target_ids[:, :-trg_len] = -100 # 只对窗口内新 token 计 loss
with torch.no_grad():
outputs = shared.model(input_ids=input_ids, labels=target_ids)
nlls.append(outputs.loss)
...
ppl = torch.exp(torch.stack(nlls).mean())
即按 stride 步长切窗,每窗口只统计窗口内新增 trg_len 个 token 的负对数似然,最终取所有窗口 NLL 的均值再指数化。max_length 优先取用户指定值,否则回退到模型 config.max_position_embeddings,再否则 2048(evaluate.py)。
6.2 为什么 llama.cpp 是 ❌:源码里有明确的守卫
矩阵的 ❌ 在代码里是显式抛错,而不是静默失败:
if shared.args.loader == "llama.cpp":
logger.error("Perplexity evaluation is not implemented for the llama.cpp loader.")
raise ValueError
原因从调用方式即可看出:整段算法依赖 shared.model(input_ids=..., labels=...) 的 HF 前向接口,且 shared.model 必须支持 labels 直接返回 loss。llama.cpp 的模型对象是封装 LlamaServer 子进程的代理,不走该接口;ExLlamav3_HF 因持有 HF 模型对象而可用;ExLlamav3 与 TensorRT-LLM 的模型对象不提供 labels 形式的 loss 输出,故均为 ❌。
6.3 数据集与结果持久化
- 内置数据集:
wikitext(wikitext-2-raw-v1 test)、ptb(Penn Treebank validation)、ptb_new(test,句间空格拼接),通过datasets.load_dataset在线加载(evaluate.py); - 自定义数据集:读取
user_data/training/datasets/{name}.txt,与 Training Tab 共用目录; - 结果存档:每次评估追加写入
user_data/logs/evaluations.csv,字段包含 Model、LoRAs、Dataset、Perplexity、stride、max_length、Date、Comment(add_entry_to_past_evaluations);相同 (model, dataset, stride, max_length) 组合会去重跳过;generate_markdown_table 按 Dataset/stride/Perplexity 排序输出对比表。这一"LoRAs"字段恰好与第三节的 LoRA 能力衔接:在 Transformers 加载器上应用若干 LoRA 后评估,可以直接在同一张表里比较基座 vs 挂 LoRA 的困惑度变化。
6.4 一个实用提示
评估前 evaluate.py 会检查 --no_use_fast:若未设置且分词耗时过长,建议以该选项重新加载模型。批量对比多个模型时(循环内逐个 unload_model + load_model,evaluate.py),确保所选模型都能被当前环境加载,否则失败的模型会打日志后跳过、不中断整批。
七、选型速查:从矩阵推导工作流
结合上文的源码证据,四条常用决策路径:
| 你的需求 | 推荐 Loader | 关键依据 |
|---|---|---|
| 日常聊天/推理,追求量化 GGUF 模型的低显存占用 | llama.cpp | 参数最丰富(loaders.py),支持 mmproj 看图 |
| 加载/应用自训 LoRA、或在 Training Tab 微调 | Transformers | 唯一同时 ✅ LoRA 加载/训练;PEFT 绑定 HF 模型(LoRA.py) |
| 对比基座/量化/微调后模型质量 | Transformers 或 ExLlamav3_HF | 唯一支持 PPL 评估的两条路线(evaluate.py) |
| 高吞吐纯文本推理(HF 格式模型) | ExLlamav3 / ExLlamav3_HF / TensorRT-LLM | 注意 ExLlamav3 才支持多模态;TensorRT-LLM 需要按官方指引单独安装引擎(models.py) |
需要注意的前提与限制:
- 本矩阵反映的是当前仓库的能力状态(对应 [docs/What Works.md](https://gitcode.com/GitHub_Trending/te/textgen/blob/79b46b80ec7ec98141c570dbc26f867fdfc39ead/docs/What Works.md?utm_source=gitcode_repo_files)),不同版本之间支持面可能变化,以仓库内文档与源码为准;
- llama.cpp 的视觉依赖模型目录中存在匹配的
mmproj-*.gguf文件,没有投影器时视觉不可用; - Transformers 的看图依赖
send_pictures扩展安装到user_data/extensions/; - 多 LoRA 合并要求各 adapter 秩相同(见 merge_loras 的警告分支),否则仅第一个生效。
八、延伸阅读
- [Model Tab 使用说明](https://gitcode.com/GitHub_Trending/te/textgen/blob/79b46b80ec7ec98141c570dbc26f867fdfc39ead/docs/04 - Model Tab.md?utm_source=gitcode_repo_files):加载器、LoRA 菜单与模型设置的界面操作;
- [多模态使用教程](https://gitcode.com/GitHub_Trending/te/textgen/blob/79b46b80ec7ec98141c570dbc26f867fdfc39ead/docs/Multimodal Tutorial.md?utm_source=gitcode_repo_files):图片聊天流程;
- [Training Tab 说明](https://gitcode.com/GitHub_Trending/te/textgen/blob/79b46b80ec7ec98141c570dbc26f867fdfc39ead/docs/05 - Training Tab.md?utm_source=gitcode_repo_files):LoRA 微调的操作细节;
- [OpenAI API 文档](https://gitcode.com/GitHub_Trending/te/textgen/blob/79b46b80ec7ec98141c570dbc26f867fdfc39ead/docs/12 - OpenAI API.md?utm_source=gitcode_repo_files):以 API 形式消费各加载器加载的模型。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00