【AIaaS 全栈架构师】第 19 篇:模型格式转换全链路——从 PyTorch 到生产部署格式
本系列定位:面向全栈架构师的 AIaaS 系统化教程。本篇为模块三"模型推理与部署优化"的第六篇,梳理模型格式转换的完整链路。技术栈以 Go 为主,Python/C++ 为辅。
本篇你将学到
- 理解为什么 PyTorch checkpoint 不能直接用于生产部署,以及四大主流生产格式的定位
- 掌握 Safetensors 格式的设计动机:如何防御 pickle 反序列化攻击
- 理解 ONNX 作为跨框架中间表示的角色,以及它的能力边界
- 掌握 GGUF 格式如何把模型权重、Tokenizer、量化元信息打包成单文件
- 理解 TensorRT Engine 的编译时优化与 GPU 架构绑定特性
- 建立格式选型决策框架与转换工具链的完整认知
学完本篇,你将能根据部署目标(vLLM、llama.cpp、TensorRT-LLM、ONNX Runtime)选择正确的目标格式,并规划从训练 checkpoint 到生产部署的完整转换链路。
一、为什么需要模型格式转换
1.1 训练格式与部署格式的鸿沟
训练阶段,几乎所有大模型都基于 PyTorch。训练结束后,你会得到一个 .pt / .pth / .bin 文件——本质都是 PyTorch 的 pickle 序列化,存储的是一个 state_dict(参数名到张量的字典)。
但生产部署时,这个格式几乎无人直接使用。原因如下:
| 安全风险 | pickle 反序列化可执行任意 Python 代码,加载不可信 .pth 等于远程代码执行 |
| 依赖重 | 加载 .pth 必须有完整 PyTorch + 模型定义代码,运行时体积大 |
| 加载慢 | pickle 反序列化慢,大模型启动耗时数分钟 |
| 无量化信息 | 原始 checkpoint 只有 FP32/FP16/BF16,没有 INT4/INT8 量化元数据 |
| 无 Tokenizer | 仅存权重,分词器、特殊 Token、对话模板需另外保存 |
| 跨框架难 | TensorFlow/JAX/C++ 推理引擎读不了 pickle |
因此从训练到生产,必须经过一次(或多次)格式转换。
1.2 四大生产部署格式全景
目前业界主流的生产部署格式有四种,分别对应不同的推理生态:
#mermaid-svg-PHzJctSMqEU9AwTU{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PHzJctSMqEU9AwTU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PHzJctSMqEU9AwTU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PHzJctSMqEU9AwTU .error-icon{fill:#552222;}#mermaid-svg-PHzJctSMqEU9AwTU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PHzJctSMqEU9AwTU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PHzJctSMqEU9AwTU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PHzJctSMqEU9AwTU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PHzJctSMqEU9AwTU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PHzJctSMqEU9AwTU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PHzJctSMqEU9AwTU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PHzJctSMqEU9AwTU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PHzJctSMqEU9AwTU .marker.cross{stroke:#333333;}#mermaid-svg-PHzJctSMqEU9AwTU svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PHzJctSMqEU9AwTU p{margin:0;}#mermaid-svg-PHzJctSMqEU9AwTU .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-PHzJctSMqEU9AwTU .cluster-label text{fill:#333;}#mermaid-svg-PHzJctSMqEU9AwTU .cluster-label span{color:#333;}#mermaid-svg-PHzJctSMqEU9AwTU .cluster-label span p{background-color:transparent;}#mermaid-svg-PHzJctSMqEU9AwTU .label text,#mermaid-svg-PHzJctSMqEU9AwTU span{fill:#333;color:#333;}#mermaid-svg-PHzJctSMqEU9AwTU .node rect,#mermaid-svg-PHzJctSMqEU9AwTU .node circle,#mermaid-svg-PHzJctSMqEU9AwTU .node ellipse,#mermaid-svg-PHzJctSMqEU9AwTU .node polygon,#mermaid-svg-PHzJctSMqEU9AwTU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PHzJctSMqEU9AwTU .rough-node .label text,#mermaid-svg-PHzJctSMqEU9AwTU .node .label text,#mermaid-svg-PHzJctSMqEU9AwTU .image-shape .label,#mermaid-svg-PHzJctSMqEU9AwTU .icon-shape .label{text-anchor:middle;}#mermaid-svg-PHzJctSMqEU9AwTU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PHzJctSMqEU9AwTU .rough-node .label,#mermaid-svg-PHzJctSMqEU9AwTU .node .label,#mermaid-svg-PHzJctSMqEU9AwTU .image-shape .label,#mermaid-svg-PHzJctSMqEU9AwTU .icon-shape .label{text-align:center;}#mermaid-svg-PHzJctSMqEU9AwTU .node.clickable{cursor:pointer;}#mermaid-svg-PHzJctSMqEU9AwTU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PHzJctSMqEU9AwTU .arrowheadPath{fill:#333333;}#mermaid-svg-PHzJctSMqEU9AwTU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PHzJctSMqEU9AwTU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PHzJctSMqEU9AwTU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PHzJctSMqEU9AwTU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PHzJctSMqEU9AwTU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PHzJctSMqEU9AwTU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PHzJctSMqEU9AwTU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PHzJctSMqEU9AwTU .cluster text{fill:#333;}#mermaid-svg-PHzJctSMqEU9AwTU .cluster span{color:#333;}#mermaid-svg-PHzJctSMqEU9AwTU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PHzJctSMqEU9AwTU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PHzJctSMqEU9AwTU rect.text{fill:none;stroke-width:0;}#mermaid-svg-PHzJctSMqEU9AwTU .icon-shape,#mermaid-svg-PHzJctSMqEU9AwTU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PHzJctSMqEU9AwTU .icon-shape p,#mermaid-svg-PHzJctSMqEU9AwTU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PHzJctSMqEU9AwTU .icon-shape .label rect,#mermaid-svg-PHzJctSMqEU9AwTU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PHzJctSMqEU9AwTU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PHzJctSMqEU9AwTU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PHzJctSMqEU9AwTU :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
转换
转换
转换
转换
被加载
被加载
被加载
被加载
PyTorch 训练checkpoint .pt/.pth
Safetensors(.safetensors)
ONNX(.onnx)
GGUF(.gguf)
TensorRT Engine(.engine/.plan)
vLLM / TGI / Transformers
ONNX Runtime / Triton
llama.cpp / Ollama / LM Studio
TensorRT-LLM / Triton
| Safetensors | HuggingFace | HF Transformers / vLLM / TGI | .safetensors |
| ONNX | Microsoft + 社区 | ONNX Runtime / Triton / OpenVINO | .onnx |
| GGUF | llama.cpp 社区 | llama.cpp / Ollama / LM Studio | .gguf |
| TensorRT Engine | NVIDIA | TensorRT-LLM / Triton | .engine / .plan |
下面逐个深入。
二、Safetensors:HuggingFace 的安全格式
2.1 设计动机:防御 pickle 攻击
PyTorch 默认用 pickle 序列化模型。Pickle 的本质是一个图灵完备的指令流——反序列化时可以执行任意 Python 代码。这意味着:
# 这是一个"恶意"的 .pth 文件
import torch
import os
class MaliciousModel:
def __reduce__(self):
return (os.system, ("rm -rf /",))
# 保存后,任何人 torch.load() 都会执行 rm -rf /
torch.save(MaliciousModel(), "evil.pth")
历史上已发生过多起通过 HuggingFace Hub 分发恶意模型的安全事件。Safetensors 就是为解决这个问题而设计的。
2.2 文件结构
Safetensors 是一个极简的二进制格式,文件结构如下:
[Header 长度(u64, 小端)] [Header JSON] [张量数据…]
Header 是一段 JSON,描述每个张量的:
- 名称(如 transformer.h.0.attn.weight)
- 数据类型(F32 / F16 / BF16 / I8 / I4 …)
- 形状(如 [4096, 4096])
- 在文件中的字节偏移量([start, end])
关键安全特性:
2.3 加载速度优势
由于 Safetensors 是 mmap 友好的零拷贝格式,加载速度比 pickle 快得多:
| PyTorch .bin(pickle) | ~25s | 峰值 28GB(反序列化副本) |
| Safetensors | ~3s | 峰值 14GB(mmap 零拷贝) |
mmap 的另一个好处:多进程共享同一份权重。在 vLLM/TGI 等多 worker 推理场景下,多个进程可以映射同一个 Safetensors 文件,物理内存只占一份。
2.4 使用方式
保存模型为 Safetensors:
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3-8B")
model.save_pretrained("./llama-8b-safetensors", safe_serialization=True)
# 默认就是 safe_serialization=True,会生成 model.safetensors
加载 Safetensors:
# Transformers 自动识别
model = AutoModelForCausalLM.from_pretrained("./llama-8b-safetensors")
# 也可以用 safetensors 库直接读张量
from safetensors.torch import load_file
tensors = load_file("./model.safetensors")
# tensors 是 dict[str, torch.Tensor]
2.5 Safetensors 的局限
Safetensors 不是万能的:
- 只存张量,不存计算图:必须配合模型代码才能用。脱离了 Transformers 库,Safetensors 文件本身不能推理
- 不支持量化元信息:只能存原始张量,INT4/INT8 量化的元数据(scale/zero-point)需另存
- 无 Tokenizer:分词器仍需另行保存(HF 用 tokenizer.json)
因此 Safetensors 适合作为 HF 生态内的中间格式,而非端到端部署格式。
三、ONNX:跨框架的中间表示
3.1 设计目标:框架无关的标准算子集
ONNX(Open Neural Network Exchange)由微软和脸书(现 Meta)在 2017 年联合推出,目标是定义一套跨框架的标准算子集和图格式。
核心思想:无论你用 PyTorch、TensorFlow、JAX 还是 PaddlePaddle 训练,都能导出成 ONNX;无论你用 ONNX Runtime、TensorRT、OpenVINO 还是 CoreML 部署,都能加载 ONNX。
#mermaid-svg-siLULcG5IZEnIQXd{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-siLULcG5IZEnIQXd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-siLULcG5IZEnIQXd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-siLULcG5IZEnIQXd .error-icon{fill:#552222;}#mermaid-svg-siLULcG5IZEnIQXd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-siLULcG5IZEnIQXd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-siLULcG5IZEnIQXd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-siLULcG5IZEnIQXd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-siLULcG5IZEnIQXd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-siLULcG5IZEnIQXd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-siLULcG5IZEnIQXd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-siLULcG5IZEnIQXd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-siLULcG5IZEnIQXd .marker.cross{stroke:#333333;}#mermaid-svg-siLULcG5IZEnIQXd svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-siLULcG5IZEnIQXd p{margin:0;}#mermaid-svg-siLULcG5IZEnIQXd .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-siLULcG5IZEnIQXd .cluster-label text{fill:#333;}#mermaid-svg-siLULcG5IZEnIQXd .cluster-label span{color:#333;}#mermaid-svg-siLULcG5IZEnIQXd .cluster-label span p{background-color:transparent;}#mermaid-svg-siLULcG5IZEnIQXd .label text,#mermaid-svg-siLULcG5IZEnIQXd span{fill:#333;color:#333;}#mermaid-svg-siLULcG5IZEnIQXd .node rect,#mermaid-svg-siLULcG5IZEnIQXd .node circle,#mermaid-svg-siLULcG5IZEnIQXd .node ellipse,#mermaid-svg-siLULcG5IZEnIQXd .node polygon,#mermaid-svg-siLULcG5IZEnIQXd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-siLULcG5IZEnIQXd .rough-node .label text,#mermaid-svg-siLULcG5IZEnIQXd .node .label text,#mermaid-svg-siLULcG5IZEnIQXd .image-shape .label,#mermaid-svg-siLULcG5IZEnIQXd .icon-shape .label{text-anchor:middle;}#mermaid-svg-siLULcG5IZEnIQXd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-siLULcG5IZEnIQXd .rough-node .label,#mermaid-svg-siLULcG5IZEnIQXd .node .label,#mermaid-svg-siLULcG5IZEnIQXd .image-shape .label,#mermaid-svg-siLULcG5IZEnIQXd .icon-shape .label{text-align:center;}#mermaid-svg-siLULcG5IZEnIQXd .node.clickable{cursor:pointer;}#mermaid-svg-siLULcG5IZEnIQXd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-siLULcG5IZEnIQXd .arrowheadPath{fill:#333333;}#mermaid-svg-siLULcG5IZEnIQXd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-siLULcG5IZEnIQXd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-siLULcG5IZEnIQXd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-siLULcG5IZEnIQXd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-siLULcG5IZEnIQXd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-siLULcG5IZEnIQXd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-siLULcG5IZEnIQXd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-siLULcG5IZEnIQXd .cluster text{fill:#333;}#mermaid-svg-siLULcG5IZEnIQXd .cluster span{color:#333;}#mermaid-svg-siLULcG5IZEnIQXd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-siLULcG5IZEnIQXd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-siLULcG5IZEnIQXd rect.text{fill:none;stroke-width:0;}#mermaid-svg-siLULcG5IZEnIQXd .icon-shape,#mermaid-svg-siLULcG5IZEnIQXd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-siLULcG5IZEnIQXd .icon-shape p,#mermaid-svg-siLULcG5IZEnIQXd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-siLULcG5IZEnIQXd .icon-shape .label rect,#mermaid-svg-siLULcG5IZEnIQXd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-siLULcG5IZEnIQXd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-siLULcG5IZEnIQXd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-siLULcG5IZEnIQXd :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
torch.onnx.export
tf2onnx
flax.to_onnx
paddle2onnx
加载
加载
加载
加载
PyTorch
ONNX 文件(.onnx)
TensorFlow
JAX/Flax
PaddlePaddle
ONNX Runtime
TensorRT
OpenVINO
CoreML
3.2 ONNX 文件结构
ONNX 是基于 Protocol Buffers 的二进制格式,主要组成:
- Graph:计算图,由节点(Node)和边(Tensor)组成
- Node:一个算子调用,如 Conv / MatMul / Softmax
- Initializer:常量张量(即模型权重)
- Operator Set(opset):算子集版本,不同 opset 支持不同算子
可视化 ONNX 文件最常用的工具是 Netron,能直观展示整个计算图。
3.3 导出流程
import torch
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3-8B")
model.eval()
dummy_input = torch.randint(0, 32000, (1, 32))
torch.onnx.export(
model,
dummy_input,
"llama-8b.onnx",
input_names=["input_ids"],
output_names=["logits"],
dynamic_axes={"input_ids": {0: "batch", 1: "sequence"}},
opset_version=16,
)
dynamic_axes 让某个维度可变(如 batch、sequence),运行时可以传不同形状的输入。
3.4 ONNX 量化
ONNX Runtime 提供了量化工具,能将 ONNX 模型动态/静态量化:
from onnxruntime.quantization import quantize_dynamic, QuantType
quantize_dynamic(
model_input="llama-8b.onnx",
model_output="llama-8b-int8.onnx",
weight_type=QuantType.QInt8,
)
ONNX 量化的优势是跨硬件通用:同一份 INT8 ONNX 可以跑在 CPU、GPU、NPU 上。
3.5 ONNX 在大模型场景的局限
ONNX 在 CNN 时代(ResNet/YOLO)非常成功,但大语言模型时代它的角色大幅萎缩:
| 大模型导出困难 | 几百层 Transformer 导出 ONNX 经常失败或不完整 |
| 动态形状支持弱 | LLM 的 KV Cache 是动态增长,ONNX 表达吃力 |
| 推理性能不如专用引擎 | ONNX Runtime 跑 LLM 的吞吐远不及 vLLM/TensorRT-LLM |
| 不支持 Continuous Batching | ONNX Runtime 没有原生的请求调度 |
| 文件巨大 | 完整 LLM 的 ONNX 文件数十 GB,加载极慢 |
结论:ONNX 适合中小型模型(如 BERT、ResNet、Whisper encoder)的跨平台部署,不推荐用于大语言模型推理。LLM 推理应优先选择 Safetensors + vLLM、GGUF + llama.cpp、TensorRT Engine + TensorRT-LLM。
四、GGUF:llama.cpp 的原生格式
4.1 设计哲学:单文件自包含
GGUF(GPT-Generated Unified Format)由 llama.cpp 作者 Georgi Gerganov 设计,目标是一个文件搞定一切:
- 模型权重(含量化)
- Tokenizer 配置
- 模型超参数(层数、注意力头数、上下文长度等)
- Chat 模板(用于对话格式化)
- 量化元数据(每层的 scale 和 zero-point)
对比 Safetensors 需要一整套文件(config.json + model.safetensors + tokenizer.json + …),GGUF 把所有东西塞进一个文件,方便分发和管理。
4.2 GGUF 的演化谱系
#mermaid-svg-trEtZ8KrBrtasvMH{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-trEtZ8KrBrtasvMH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-trEtZ8KrBrtasvMH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-trEtZ8KrBrtasvMH .error-icon{fill:#552222;}#mermaid-svg-trEtZ8KrBrtasvMH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-trEtZ8KrBrtasvMH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-trEtZ8KrBrtasvMH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-trEtZ8KrBrtasvMH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-trEtZ8KrBrtasvMH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-trEtZ8KrBrtasvMH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-trEtZ8KrBrtasvMH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-trEtZ8KrBrtasvMH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-trEtZ8KrBrtasvMH .marker.cross{stroke:#333333;}#mermaid-svg-trEtZ8KrBrtasvMH svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-trEtZ8KrBrtasvMH p{margin:0;}#mermaid-svg-trEtZ8KrBrtasvMH .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-trEtZ8KrBrtasvMH .cluster-label text{fill:#333;}#mermaid-svg-trEtZ8KrBrtasvMH .cluster-label span{color:#333;}#mermaid-svg-trEtZ8KrBrtasvMH .cluster-label span p{background-color:transparent;}#mermaid-svg-trEtZ8KrBrtasvMH .label text,#mermaid-svg-trEtZ8KrBrtasvMH span{fill:#333;color:#333;}#mermaid-svg-trEtZ8KrBrtasvMH .node rect,#mermaid-svg-trEtZ8KrBrtasvMH .node circle,#mermaid-svg-trEtZ8KrBrtasvMH .node ellipse,#mermaid-svg-trEtZ8KrBrtasvMH .node polygon,#mermaid-svg-trEtZ8KrBrtasvMH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-trEtZ8KrBrtasvMH .rough-node .label text,#mermaid-svg-trEtZ8KrBrtasvMH .node .label text,#mermaid-svg-trEtZ8KrBrtasvMH .image-shape .label,#mermaid-svg-trEtZ8KrBrtasvMH .icon-shape .label{text-anchor:middle;}#mermaid-svg-trEtZ8KrBrtasvMH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-trEtZ8KrBrtasvMH .rough-node .label,#mermaid-svg-trEtZ8KrBrtasvMH .node .label,#mermaid-svg-trEtZ8KrBrtasvMH .image-shape .label,#mermaid-svg-trEtZ8KrBrtasvMH .icon-shape .label{text-align:center;}#mermaid-svg-trEtZ8KrBrtasvMH .node.clickable{cursor:pointer;}#mermaid-svg-trEtZ8KrBrtasvMH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-trEtZ8KrBrtasvMH .arrowheadPath{fill:#333333;}#mermaid-svg-trEtZ8KrBrtasvMH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-trEtZ8KrBrtasvMH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-trEtZ8KrBrtasvMH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-trEtZ8KrBrtasvMH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-trEtZ8KrBrtasvMH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-trEtZ8KrBrtasvMH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-trEtZ8KrBrtasvMH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-trEtZ8KrBrtasvMH .cluster text{fill:#333;}#mermaid-svg-trEtZ8KrBrtasvMH .cluster span{color:#333;}#mermaid-svg-trEtZ8KrBrtasvMH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-trEtZ8KrBrtasvMH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-trEtZ8KrBrtasvMH rect.text{fill:none;stroke-width:0;}#mermaid-svg-trEtZ8KrBrtasvMH .icon-shape,#mermaid-svg-trEtZ8KrBrtasvMH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-trEtZ8KrBrtasvMH .icon-shape p,#mermaid-svg-trEtZ8KrBrtasvMH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-trEtZ8KrBrtasvMH .icon-shape .label rect,#mermaid-svg-trEtZ8KrBrtasvMH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-trEtZ8KrBrtasvMH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-trEtZ8KrBrtasvMH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-trEtZ8KrBrtasvMH :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
重构
扩展
已被淘汰,新模型不再使用
GGML(2023初)
GGUF v1(2023年中)
GGUF v2/v3(2023末至今)
废弃
老格式 GGML 已被废弃,新模型只发布 GGUF。GGUF 的关键改进是可扩展的 key-value 元数据结构——添加新字段不破坏旧版本兼容性。
4.3 量化方案
GGUF 内置了一套丰富的量化方案,称为 k-quants(第 17 篇详细讨论过)。常用方案:
| Q8_0 | 8 bit | 7.0 GB | 极小 | 质量优先 |
| Q6_K | 6 bit | 5.5 GB | 很小 | 平衡 |
| Q5_K_M | 5 bit | 4.8 GB | 小 | 推荐 |
| Q4_K_M | 4 bit | 4.1 GB | 中等 | 最常用 |
| Q4_0 | 4 bit | 3.8 GB | 较大 | 极致压缩 |
| Q3_K_M | 3 bit | 3.3 GB | 明显 | 显存极紧 |
| Q2_K | 2 bit | 2.7 GB | 较大 | 极限场景 |
_K_M 后缀(Medium)是混合精度变体:注意力层用高精度,MLP 用低精度,整体质量比纯 Q4_0 更好。
4.4 转换工具链
从 HuggingFace 模型转 GGUF 的标准流程:
# 1. 克隆 llama.cpp
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
# 2. 安装 Python 依赖
pip install -r requirements.txt
# 3. 转换(FP16 中间格式)
python convert_hf_to_gguf.py \\
/path/to/hf-model \\
–outfile model-fp16.gguf \\
–outtype f16
# 4. 量化(k-quants)
./llama-quantize model-fp16.gguf model-q4_k_m.gguf Q4_K_M
# 5. 验证
./llama-cli -m model-q4_k_m.gguf -p "你好" -n 32
关键点:
- convert_hf_to_gguf.py 支持 Llama / Qwen / Mistral / Yi / DeepSeek 等主流架构
- 量化前必须先转成 FP16 GGUF 中间格式
- 不同量化级别用同一个 FP16 中间文件生成,便于对比
4.5 GGUF 的运行生态
GGUF 已成为端侧 LLM 的事实标准:
| llama.cpp | 全平台 | 原生实现,C++ 编写,支持 CPU/GPU 混合 |
| Ollama | macOS/Linux/Windows | 基于 llama.cpp,自动管理模型,类似 Docker 体验 |
| LM Studio | macOS/Windows | 桌面 GUI,从 Hub 下载 GGUF 即用 |
| GPT4All | 全平台 | 桌面应用,内置模型库 |
| llamafile | 全平台 | 单可执行文件(GGUF + 运行时打包) |
4.6 GGUF 的 CPU/GPU 混合推理
GGUF 的杀手锏是层卸载(Layer Offload):模型太大 GPU 放不下时,可以把一部分层放 GPU、其余放 CPU,逐步推理。
# ngl = number of GPU layers
./llama-cli -m model-q4_k_m.gguf -p "你好" -n 32 -ngl 20
比如 70B Q4 模型(35GB),如果 GPU 只有 16GB 显存,可以把前 20 层放 GPU、剩余 40 层放 CPU 内存。速度虽不如全 GPU,但能跑起来——这是其他格式做不到的。
五、TensorRT Engine:NVIDIA 的极致优化格式
5.1 设计目标:极致推理性能
TensorRT 是 NVIDIA 推出的深度学习推理优化器,其输出是 Engine 文件(也叫 Plan 文件)。Engine 是经过深度优化的二进制,包含:
- 算子融合(Kernel Fusion):多个小算子合并成一个大算子,减少 kernel 启动开销
- 精度校准(Precision Calibration):自动选择 FP32/FP16/INT8
- Kernel 自动调优:针对当前 GPU 选择最快的 kernel 实现
- 显存规划:预分配所有中间张量内存,运行时零分配
5.2 Engine 的关键特性:硬件绑定
TensorRT Engine 是编译时绑定 GPU 架构的。这意味着:
| GPU 架构(如 Ampere/Hopper) | 绑定 |
| 具体型号(如 A100 vs A10) | 不绑定(同架构通用) |
| CUDA 版本 | 绑定 |
| TensorRT 版本 | 绑定 |
| Batch Size | 部分绑定(需指定 optimization profile) |
实践影响:
- 在 A100 上编译的 Engine,不能直接拿到 H100 上跑
- 升级 TensorRT 版本后,旧 Engine 可能失效,需重新编译
- 这与 Safetensors/GGUF 的"一份文件到处跑"形成鲜明对比
5.3 编译流程
#mermaid-svg-5DCKX8UUvFsbfXG6{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5DCKX8UUvFsbfXG6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5DCKX8UUvFsbfXG6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5DCKX8UUvFsbfXG6 .error-icon{fill:#552222;}#mermaid-svg-5DCKX8UUvFsbfXG6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5DCKX8UUvFsbfXG6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5DCKX8UUvFsbfXG6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5DCKX8UUvFsbfXG6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5DCKX8UUvFsbfXG6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5DCKX8UUvFsbfXG6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5DCKX8UUvFsbfXG6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5DCKX8UUvFsbfXG6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5DCKX8UUvFsbfXG6 .marker.cross{stroke:#333333;}#mermaid-svg-5DCKX8UUvFsbfXG6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5DCKX8UUvFsbfXG6 p{margin:0;}#mermaid-svg-5DCKX8UUvFsbfXG6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-5DCKX8UUvFsbfXG6 .cluster-label text{fill:#333;}#mermaid-svg-5DCKX8UUvFsbfXG6 .cluster-label span{color:#333;}#mermaid-svg-5DCKX8UUvFsbfXG6 .cluster-label span p{background-color:transparent;}#mermaid-svg-5DCKX8UUvFsbfXG6 .label text,#mermaid-svg-5DCKX8UUvFsbfXG6 span{fill:#333;color:#333;}#mermaid-svg-5DCKX8UUvFsbfXG6 .node rect,#mermaid-svg-5DCKX8UUvFsbfXG6 .node circle,#mermaid-svg-5DCKX8UUvFsbfXG6 .node ellipse,#mermaid-svg-5DCKX8UUvFsbfXG6 .node polygon,#mermaid-svg-5DCKX8UUvFsbfXG6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5DCKX8UUvFsbfXG6 .rough-node .label text,#mermaid-svg-5DCKX8UUvFsbfXG6 .node .label text,#mermaid-svg-5DCKX8UUvFsbfXG6 .image-shape .label,#mermaid-svg-5DCKX8UUvFsbfXG6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-5DCKX8UUvFsbfXG6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5DCKX8UUvFsbfXG6 .rough-node .label,#mermaid-svg-5DCKX8UUvFsbfXG6 .node .label,#mermaid-svg-5DCKX8UUvFsbfXG6 .image-shape .label,#mermaid-svg-5DCKX8UUvFsbfXG6 .icon-shape .label{text-align:center;}#mermaid-svg-5DCKX8UUvFsbfXG6 .node.clickable{cursor:pointer;}#mermaid-svg-5DCKX8UUvFsbfXG6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5DCKX8UUvFsbfXG6 .arrowheadPath{fill:#333333;}#mermaid-svg-5DCKX8UUvFsbfXG6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5DCKX8UUvFsbfXG6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5DCKX8UUvFsbfXG6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5DCKX8UUvFsbfXG6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5DCKX8UUvFsbfXG6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5DCKX8UUvFsbfXG6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5DCKX8UUvFsbfXG6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5DCKX8UUvFsbfXG6 .cluster text{fill:#333;}#mermaid-svg-5DCKX8UUvFsbfXG6 .cluster span{color:#333;}#mermaid-svg-5DCKX8UUvFsbfXG6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5DCKX8UUvFsbfXG6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5DCKX8UUvFsbfXG6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-5DCKX8UUvFsbfXG6 .icon-shape,#mermaid-svg-5DCKX8UUvFsbfXG6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5DCKX8UUvFsbfXG6 .icon-shape p,#mermaid-svg-5DCKX8UUvFsbfXG6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5DCKX8UUvFsbfXG6 .icon-shape .label rect,#mermaid-svg-5DCKX8UUvFsbfXG6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5DCKX8UUvFsbfXG6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5DCKX8UUvFsbfXG6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5DCKX8UUvFsbfXG6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
转换
trtexec 编译
加载
PyTorch/.pth或 ONNX
ONNX 中间格式(.onnx)
TensorRT Engine(.engine)
TensorRT Runtime推理
使用 trtexec 命令行工具编译:
# 从 ONNX 编译 Engine
trtexec \\
–onnx=model.onnx \\
–saveEngine=model.engine \\
–fp16 \\
–minShapes=input_ids:1×32 \\
–optShapes=input_ids:8×512 \\
–maxShapes=input_ids:32×2048 \\
–verbose
关键参数:
- –fp16 / –int8:选择精度
- –minShapes / –optShapes / –maxShapes:定义动态形状范围(optimization profile),编译器据此选择最优 kernel
- –workspace:编译期最大 workspace 显存
5.4 TensorRT-LLM:针对 LLM 的专用方案
通用 TensorRT 编译 LLM 非常复杂(要手动实现 KV Cache、Attention 等算子)。NVIDIA 推出了 TensorRT-LLM,专门为 LLM 推理优化:
- 内置 FlashAttention、PagedAttention 的 TensorRT 实现
- 自动处理 KV Cache 管理
- 支持 Continuous Batching、投机解码
- 提供 Python API 简化编译
import tensorrt_llm
from tensorrt_llm.models import LLaMAForCausalLM
# 编译 Llama 为 TensorRT Engine
builder = tensorrt_llm.Builder()
model = LLaMAForCausalLM.from_hugging_face("meta-llama/Llama-3-8B")
engine = builder.build(
model,
precision="fp16",
max_batch_size=32,
max_seq_len=4096,
)
engine.save("llama-8b.engine")
TensorRT-LLM 在 NVIDIA GPU 上的吞吐量通常是 vLLM 的 1.2-1.5 倍(同硬件),是追求极致性能时的首选。
5.5 Engine 的局限
| 硬件绑定 | 跨架构迁移需重新编译,编译耗时数十分钟到数小时 |
| 版本绑定 | TensorRT 升级后旧 Engine 可能不兼容 |
| 闭源 | TensorRT 本身闭源,调试困难 |
| 仅 NVIDIA | 不能在 AMD/Intel GPU 或 CPU 上运行 |
| 不可读 | Engine 是纯二进制,无法反推模型结构 |
六、四大格式横向对比
| 设计目标 | 安全的权重存储 | 跨框架中间表示 | 端侧单文件部署 | 极致 NVIDIA GPU 性能 |
| 是否含计算图 | 否(仅权重) | 是(完整计算图) | 否(权重+超参) | 是(已编译) |
| 是否含 Tokenizer | 否 | 否 | 是 | 否 |
| 是否含量化元数据 | 否 | 部分 | 是(k-quants) | 是 |
| 文件大小(7B FP16) | ~14 GB | ~14 GB+图开销 | ~14 GB / ~4 GB(Q4) | ~14 GB+优化数据 |
| 加载速度 | 快(mmap) | 慢 | 快 | 快(已编译) |
| 推理性能 | 取决于引擎 | 中等 | 中等(CPU 优秀) | 极致(NVIDIA) |
| 跨硬件 | 取决于引擎 | 优秀 | 优秀(CPU/GPU 混合) | 仅 NVIDIA GPU |
| 安全性 | 高(无代码执行) | 中等 | 高 | 高 |
| 可读性 | 中(Header JSON 可读) | 高(Netron 可视化) | 中 | 无(纯二进制) |
| 生态绑定 | HuggingFace | 跨平台 | llama.cpp | NVIDIA |
| 适合模型类型 | 大模型 | 中小模型 | 大模型 | 大模型(NVIDIA) |
七、格式选型决策树
#mermaid-svg-ExksxvOX6TijqCbd{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ExksxvOX6TijqCbd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ExksxvOX6TijqCbd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ExksxvOX6TijqCbd .error-icon{fill:#552222;}#mermaid-svg-ExksxvOX6TijqCbd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ExksxvOX6TijqCbd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ExksxvOX6TijqCbd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ExksxvOX6TijqCbd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ExksxvOX6TijqCbd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ExksxvOX6TijqCbd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ExksxvOX6TijqCbd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ExksxvOX6TijqCbd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ExksxvOX6TijqCbd .marker.cross{stroke:#333333;}#mermaid-svg-ExksxvOX6TijqCbd svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ExksxvOX6TijqCbd p{margin:0;}#mermaid-svg-ExksxvOX6TijqCbd .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ExksxvOX6TijqCbd .cluster-label text{fill:#333;}#mermaid-svg-ExksxvOX6TijqCbd .cluster-label span{color:#333;}#mermaid-svg-ExksxvOX6TijqCbd .cluster-label span p{background-color:transparent;}#mermaid-svg-ExksxvOX6TijqCbd .label text,#mermaid-svg-ExksxvOX6TijqCbd span{fill:#333;color:#333;}#mermaid-svg-ExksxvOX6TijqCbd .node rect,#mermaid-svg-ExksxvOX6TijqCbd .node circle,#mermaid-svg-ExksxvOX6TijqCbd .node ellipse,#mermaid-svg-ExksxvOX6TijqCbd .node polygon,#mermaid-svg-ExksxvOX6TijqCbd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ExksxvOX6TijqCbd .rough-node .label text,#mermaid-svg-ExksxvOX6TijqCbd .node .label text,#mermaid-svg-ExksxvOX6TijqCbd .image-shape .label,#mermaid-svg-ExksxvOX6TijqCbd .icon-shape .label{text-anchor:middle;}#mermaid-svg-ExksxvOX6TijqCbd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ExksxvOX6TijqCbd .rough-node .label,#mermaid-svg-ExksxvOX6TijqCbd .node .label,#mermaid-svg-ExksxvOX6TijqCbd .image-shape .label,#mermaid-svg-ExksxvOX6TijqCbd .icon-shape .label{text-align:center;}#mermaid-svg-ExksxvOX6TijqCbd .node.clickable{cursor:pointer;}#mermaid-svg-ExksxvOX6TijqCbd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ExksxvOX6TijqCbd .arrowheadPath{fill:#333333;}#mermaid-svg-ExksxvOX6TijqCbd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ExksxvOX6TijqCbd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ExksxvOX6TijqCbd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ExksxvOX6TijqCbd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ExksxvOX6TijqCbd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ExksxvOX6TijqCbd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ExksxvOX6TijqCbd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ExksxvOX6TijqCbd .cluster text{fill:#333;}#mermaid-svg-ExksxvOX6TijqCbd .cluster span{color:#333;}#mermaid-svg-ExksxvOX6TijqCbd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ExksxvOX6TijqCbd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ExksxvOX6TijqCbd rect.text{fill:none;stroke-width:0;}#mermaid-svg-ExksxvOX6TijqCbd .icon-shape,#mermaid-svg-ExksxvOX6TijqCbd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ExksxvOX6TijqCbd .icon-shape p,#mermaid-svg-ExksxvOX6TijqCbd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ExksxvOX6TijqCbd .icon-shape .label rect,#mermaid-svg-ExksxvOX6TijqCbd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ExksxvOX6TijqCbd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ExksxvOX6TijqCbd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ExksxvOX6TijqCbd :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
NVIDIA 服务器追求极致性能
NVIDIA 服务器通用方案
CPU 或消费级 GPU端侧部署
跨硬件/嵌入式中小模型
有训练好的 PyTorch 模型需要部署
部署目标?
TensorRT Engine用 TensorRT-LLM 编译
Safetensors用 vLLM/TGI 加载
GGUF用 llama.cpp/Ollama
ONNX用 ONNX Runtime
注意:硬件绑定换 GPU 架构需重编译
主流方案HF 模型默认 Safetensors
端侧事实标准单文件分发
不推荐用于大语言模型
7.1 典型场景对照
场景 A:云端 LLM 服务(A100/H100 集群)
- 目标格式:Safetensors(基础) + 可选 TensorRT Engine(极致)
- 工具链:HF Transformers 训练 → save_pretrained(safe_serialization=True) → vLLM 加载
- 极致优化:用 TensorRT-LLM 编译 Engine,吞吐再提 20-50%
场景 B:私有化部署(消费级 GPU 或纯 CPU)
- 目标格式:GGUF(Q4_K_M 或 Q5_K_M)
- 工具链:HF 模型 → convert_hf_to_gguf.py → llama-quantize → Ollama 分发
- 优势:单文件、跨平台、CPU/GPU 混合
场景 C:移动端/嵌入式(手机、边缘设备)
- 目标格式:ONNX 或专用格式(CoreML/TFLite)
- 工具链:PyTorch → torch.onnx.export → ONNX Runtime Mobile
- 局限:仅适合小模型(< 1B 参数)
场景 D:多框架/多硬件通用
- 目标格式:ONNX
- 工具链:任意框架 → ONNX → 多种运行时
- 局限:LLM 场景不推荐
八、完整转换链路实战
8.1 从 HF 模型到 vLLM(Safetensors 链路)
最简单的链路,HF Hub 上大多数模型已是 Safetensors 格式:
# 直接用 vLLM 启动
python -m vllm.entrypoints.openai.api_server \\
–model meta-llama/Llama-3-8B-Instruct \\
–tensor-parallel-size 1
# vLLM 自动从 HF Hub 下载并加载 Safetensors
如果你的模型是自训练的,需先保存为 Safetensors:
model.save_pretrained("./my-model", safe_serialization=True)
# 生成: config.json, model.safetensors, tokenizer.json
8.2 从 HF 模型到 llama.cpp(GGUF 链路)
# 1. 下载 HF 模型
huggingface-cli download meta-llama/Llama-3-8B-Instruct \\
–local-dir ./llama-3-8b
# 2. 转 FP16 GGUF
cd llama.cpp
python convert_hf_to_gguf.py ../llama-3-8b \\
–outfile ../llama-3-8b-fp16.gguf \\
–outtype f16
# 3. 量化
./llama-quantize ../llama-3-8b-fp16.gguf ../llama-3-8b-q4_k_m.gguf Q4_K_M
# 4. 运行
./llama-cli -m ../llama-3-8b-q4_k_m.gguf -p "你好" -ngl 99
# 5. 或用 Ollama 导入
cat > Modelfile <<EOF
FROM ./llama-3-8b-q4_k_m.gguf
EOF
ollama create my-llama -f Modelfile
ollama run my-llama
8.3 从 HF 模型到 TensorRT-LLM(Engine 链路)
最复杂的链路:
# 1. 克隆 TensorRT-LLM
git clone https://github.com/NVIDIA/TensorRT-LLM.git
cd TensorRT-LLM
# 2. 用提供的脚本转换
python examples/llama/convert_checkpoint.py \\
–model_dir ./llama-3-8b-hf \\
–output_dir ./llama-3-8b-trt-checkpoint \\
–dtype float16
# 3. 编译 Engine
python examples/llama/build.py \\
–checkpoint_dir ./llama-3-8b-trt-checkpoint \\
–output_dir ./llama-3-8b-engine \\
–use_gpt_attention_plugin float16 \\
–use_gemm_plugin float16 \\
–max_batch_size 32 \\
–max_input_len 2048 \\
–max_output_len 512
# 4. 运行
python examples/llama/run.py \\
–engine_dir ./llama-3-8b-engine \\
–tokenizer_dir ./llama-3-8b-hf
8.4 链路对比
| Safetensors → vLLM | 极简 | 0(HF 已是此格式) | 极简 | 优秀 |
| HF → GGUF → llama.cpp | 简单 | ~10 分钟 | 简单 | 中等(CPU 优秀) |
| HF → ONNX | 中等 | ~30 分钟 | 中等 | 中等 |
| HF → TensorRT Engine | 复杂 | ~1-2 小时 | 复杂 | 极致 |
九、格式转换的常见坑
9.1 Tokenizer 不一致
坑:转换格式后,Tokenizer 配置丢失或不一致,导致输出乱码或质量下降。
表现:
- GGUF 转换后,特殊 Token(如 <|im_start|>)未被识别
- Safetensors 跨库加载时,BOS/EOS Token 配置不一致
- TensorRT-LLM 编译时未指定正确 tokenizer
对策:
- GGUF 转换用最新的 convert_hf_to_gguf.py,它会自动从 tokenizer_config.json 读取特殊 Token
- 部署后用固定 prompt 测试,确认 BOS/EOS 行为正确
- TensorRT-LLM 需显式指定 –tokenizer_dir
9.2 量化精度回退
坑:跨格式转换时量化精度意外回退到 FP16。
表现:
- GGUF Q4 模型转 ONNX 后变成 FP16(丢失量化)
- ONNX INT8 模型转 TensorRT 后精度异常
对策:
- 转换前后用 llama-cli 或推理脚本对比 perplexity,差异应 < 1%
- 不同格式之间不要直接互转量化模型,应回到 FP16 中间格式重新量化
9.3 TensorRT Engine 跨机器失效
坑:在开发机编译的 Engine,部署到生产机报错。
表现:
[TensorRT] INTERNAL ERROR: Assertion failed: … engine expects SM 8.6, but device is SM 8.0
对策:
- Engine 必须在目标硬件上编译
- CI/CD 流水线用 Docker 镜像固定 TensorRT 版本,编译时挂载目标 GPU
- 或者:分发 ONNX/Safetensors,在目标机器上现场编译 Engine
9.4 大模型分片问题
坑:Safetensors 大模型通常分片存储(model-00001-of-00005.safetensors),转换工具可能不识别分片。
对策:
- 用 HF Transformers 的 from_pretrained 自动合并分片
- GGUF 转换脚本已支持分片输入
- TensorRT-LLM 的 convert_checkpoint.py 也支持
9.5 BF16 兼容性
坑:BF16(Brain Float 16)模型在只支持 FP16 的硬件/格式上报错。
表现:
- BF16 模型转 GGUF Q4 后精度异常
- BF16 ONNX 在旧版 ONNX Runtime 报错
对策:
- 转换前先 torch.float16 转一下(精度损失极小)
- GGUF 转换脚本用 –outtype f16 显式指定 FP16
十、模型仓库的组织规范
生产环境中,同一模型可能需要多种格式并存。推荐的目录结构:
models/my-llama-8b/
├── hf/ # HuggingFace 格式(源)
│ ├── config.json
│ ├── model.safetensors
│ ├── tokenizer.json
│ └── …
├── gguf/
│ ├── my-llama-8b-q5_k_m.gguf # 平衡版
│ └── my-llama-8b-q4_k_m.gguf # 压缩版
├── trt/
│ ├── my-llama-8b-a100.engine # A100 专用
│ └── my-llama-8b-h100.engine # H100 专用
├── onnx/
│ └── my-llama-8b-int8.onnx # ONNX(如需要)
├── README.md # 模型说明
└── checksums.sha256 # 校验和
规范要点:
十一、自动化转换流水线
在 AIaaS 平台中,模型格式转换应自动化。典型的 CI 流水线:
#mermaid-svg-3eTlsXBWASHUOkN1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3eTlsXBWASHUOkN1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3eTlsXBWASHUOkN1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3eTlsXBWASHUOkN1 .error-icon{fill:#552222;}#mermaid-svg-3eTlsXBWASHUOkN1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3eTlsXBWASHUOkN1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3eTlsXBWASHUOkN1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3eTlsXBWASHUOkN1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3eTlsXBWASHUOkN1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3eTlsXBWASHUOkN1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3eTlsXBWASHUOkN1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3eTlsXBWASHUOkN1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3eTlsXBWASHUOkN1 .marker.cross{stroke:#333333;}#mermaid-svg-3eTlsXBWASHUOkN1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3eTlsXBWASHUOkN1 p{margin:0;}#mermaid-svg-3eTlsXBWASHUOkN1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-3eTlsXBWASHUOkN1 .cluster-label text{fill:#333;}#mermaid-svg-3eTlsXBWASHUOkN1 .cluster-label span{color:#333;}#mermaid-svg-3eTlsXBWASHUOkN1 .cluster-label span p{background-color:transparent;}#mermaid-svg-3eTlsXBWASHUOkN1 .label text,#mermaid-svg-3eTlsXBWASHUOkN1 span{fill:#333;color:#333;}#mermaid-svg-3eTlsXBWASHUOkN1 .node rect,#mermaid-svg-3eTlsXBWASHUOkN1 .node circle,#mermaid-svg-3eTlsXBWASHUOkN1 .node ellipse,#mermaid-svg-3eTlsXBWASHUOkN1 .node polygon,#mermaid-svg-3eTlsXBWASHUOkN1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3eTlsXBWASHUOkN1 .rough-node .label text,#mermaid-svg-3eTlsXBWASHUOkN1 .node .label text,#mermaid-svg-3eTlsXBWASHUOkN1 .image-shape .label,#mermaid-svg-3eTlsXBWASHUOkN1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-3eTlsXBWASHUOkN1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3eTlsXBWASHUOkN1 .rough-node .label,#mermaid-svg-3eTlsXBWASHUOkN1 .node .label,#mermaid-svg-3eTlsXBWASHUOkN1 .image-shape .label,#mermaid-svg-3eTlsXBWASHUOkN1 .icon-shape .label{text-align:center;}#mermaid-svg-3eTlsXBWASHUOkN1 .node.clickable{cursor:pointer;}#mermaid-svg-3eTlsXBWASHUOkN1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3eTlsXBWASHUOkN1 .arrowheadPath{fill:#333333;}#mermaid-svg-3eTlsXBWASHUOkN1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3eTlsXBWASHUOkN1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3eTlsXBWASHUOkN1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3eTlsXBWASHUOkN1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3eTlsXBWASHUOkN1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3eTlsXBWASHUOkN1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3eTlsXBWASHUOkN1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3eTlsXBWASHUOkN1 .cluster text{fill:#333;}#mermaid-svg-3eTlsXBWASHUOkN1 .cluster span{color:#333;}#mermaid-svg-3eTlsXBWASHUOkN1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3eTlsXBWASHUOkN1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3eTlsXBWASHUOkN1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-3eTlsXBWASHUOkN1 .icon-shape,#mermaid-svg-3eTlsXBWASHUOkN1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3eTlsXBWASHUOkN1 .icon-shape p,#mermaid-svg-3eTlsXBWASHUOkN1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3eTlsXBWASHUOkN1 .icon-shape .label rect,#mermaid-svg-3eTlsXBWASHUOkN1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3eTlsXBWASHUOkN1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3eTlsXBWASHUOkN1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3eTlsXBWASHUOkN1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
训练完成推送 HF 格式
触发转换流水线
Job 1: Safetensors校验 + 上传仓库
Job 2: GGUF多量化级别
Job 3: TRT Engine多 GPU 架构
模型仓库
通知部署系统新版本可用
关键设计:
- HF 格式上传后自动触发所有下游转换 Job
- 各 Job 并行执行,互不阻塞
- 每个 Job 输出标准化的 manifest(量化级别、硬件、性能基准)
- 失败的 Job 不阻塞其他 Job,但触发告警
十二、格式演进趋势
12.1 Safetensors 的统治地位加强
随着 vLLM、TGI 等主流推理引擎的普及,Safetensors 已成为云端 LLM 推理的事实标准。HuggingFace Hub 上新发布的大模型几乎全部默认 Safetensors。
12.2 GGUF 在端侧继续扩张
GGUF 是端侧部署(Ollama、LM Studio、桌面应用)的唯一选择。随着消费级 GPU 显存增大(RTX 5090 32GB),Q4 量化的 70B 模型也能在桌面跑起来,GGUF 生态持续繁荣。
12.3 ONNX 在 LLM 领域收缩
ONNX 在中小模型(CV、语音、中小文本模型)仍有一席之地,但 LLM 领域基本退出主流。Microsoft 自家的 ONNX Runtime 也在转向支持 GGUF 作为 LLM 后端。
12.4 TensorRT Engine 锁定高端
TensorRT-LLM 在 NVIDIA 高端 GPU 集群(A100/H100/B200)上是性能王者,但其硬件绑定和闭源特性限制了普及。AMD 的 ROCm 生态、Intel 的 GPU 生态在追赶,但短期内 NVIDIA 优势明显。
12.5 新兴格式:Mamba/SSM 专用格式
随着 Mamba、RWKV 等非 Transformer 架构兴起,传统格式(基于 Transformer 假设设计的)面临挑战。未来可能出现针对 SSM(State Space Model)优化的新格式。
本篇小结
| 格式转换必要性 | PyTorch pickle 不安全、依赖重、加载慢,生产必须转换 |
| Safetensors | HuggingFace 安全格式,零拷贝 mmap 加载,防 pickle 攻击,是云端 LLM 主流 |
| ONNX | 跨框架中间表示,Protocol Buffers 格式,适合中小模型跨平台部署,LLM 场景不推荐 |
| GGUF | llama.cpp 原生格式,单文件自包含(权重+Tokenizer+量化),端侧事实标准 |
| GGUF k-quants | 丰富的量化方案(Q4_K_M 最常用),CPU/GPU 混合推理(层卸载) |
| TensorRT Engine | NVIDIA 编译时优化产物,极致性能但硬件绑定,需用 TensorRT-LLM 编译 LLM |
| 选型决策 | 云端 NVIDIA→Safetensors 或 TRT Engine;端侧→GGUF;跨硬件中小模型→ONNX |
| 转换链路 | HF 是 source of truth,所有格式从 HF 转换,不要跨格式互转量化模型 |
| 工程规范 | 模型仓库按格式分子目录,Engine 标注硬件,保存校验和和转换脚本 |
| 自动化 | 转换流水线化,HF 上传触发并行 Job,输出标准化 manifest |
下篇预告
第 20 篇:推理部署实战——用 Go 构建 vLLM 推理服务客户端
模块三讨论了推理引擎、KV Cache、Continuous Batching、量化、投机解码、模型格式转换等核心优化技术,但都是站在引擎内部视角。下一篇我们切换到外部调用方视角,用 Go 语言构建一个生产级的 vLLM 推理客户端——调用 OpenAI 兼容 API、处理流式 SSE 响应、gRPC 推理、健康检查、连接池管理,所有代码完整可运行,把前面学到的理论落地到工程。
如果本篇内容对你有帮助,欢迎点赞收藏!有任何疑问,欢迎在评论区交流。

![[特殊字符]DeepSeek‑Harness(DSH)小白保姆教程-171主机测评](https://www.171host.com/wp-content/uploads/2026/08/20260816085112-6a817a009aabf-220x150.png)