从 `.pt` 到 `.gguf`:彻底理解大模型权重格式与推理框架的关系

封面

很多人刚接触大模型都会有一个疑问:

模型权重不是 .pt.pth 吗?为什么现在又冒出来 .safetensors.gguf.engine

为什么同一个模型,必须使用不同的组件(Transformers、vLLM、Ollama、llama.cpp)来启动?底层不都是 PyTorch 吗?

本文将从 训练 → 导出 → 推理 的完整生命周期,带你彻底理解这些问题。

一个最大的误区

很多人认为:

模型 = PyTorch 权重

其实这是错误的。

真正应该理解为:

模型(Model) = 神经网络结构 + 参数(Weights)

PyTorch 只是训练模型的一种框架,而不是模型本身。

可以把它理解成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PyTorch
TensorFlow
JAX



训练出了同一个模型



模型可以导出成很多种格式



不同推理引擎加载

真正重要的是:

模型和运行框架是分离的。


大模型的完整生命周期

整个流程其实非常简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
           训练阶段


PyTorch / JAX / DeepSpeed


得到原始模型权重
(.bin / .pt / .pth / safetensors)

可以继续训练 / 微调


转换成各种推理格式
GGUF / TensorRT / CoreML ...


llama.cpp / vLLM / Ollama /
TensorRT-LLM / MLX ...

可以发现:

训练格式和推理格式本来就是两个世界。


第一阶段:训练

目前几乎所有主流大模型:

  • Qwen
  • Llama
  • DeepSeek
  • Gemma
  • Mistral

都是使用:

  • PyTorch
  • JAX
  • Megatron-LM
  • DeepSpeed
  • FSDP

训练出来的。

训练结束以后,通常得到的是:

1
pytorch_model.bin

或者

1
model.safetensors

早几年最常见的是:

1
pytorch_model.bin

实际上就是:

1
torch.save(model.state_dict())

保存出来的。

后来因为:

  • pickle 不安全
  • 加载速度较慢
  • 不支持 mmap

于是大家开始统一使用:

1
.safetensors

例如 Hugging Face 下载的模型:

1
2
3
4
5
6
7
8
9
Qwen3/

├── config.json
├── tokenizer.json
├── tokenizer_config.json
├── generation_config.json
├── model-00001-of-00008.safetensors
├── model-00002-of-00008.safetensors
└── ...

.pt.pth.bin.safetensors 有什么区别?

很多新人会觉得这些都是不同格式。

其实并不是。

.pt / .pth

一般都是:

1
torch.save(...)

保存出来的。

可以保存:

  • 整个模型
  • state_dict
  • optimizer
  • scheduler
  • checkpoint

例如:

1
2
3
4
torch.save({
"model": model.state_dict(),
"optimizer": optimizer.state_dict()
}, "checkpoint.pt")

所以:

.pt 并不一定只是模型权重。


pytorch_model.bin

Hugging Face 以前默认使用:

1
pytorch_model.bin

本质也是:

1
torch.save(state_dict)

只是名字叫:

1
pytorch_model.bin

实际上依旧是 pickle。


safetensors

现在已经成为 Hugging Face 主流格式。

优点:

  • 安全(没有 pickle)
  • mmap 加载
  • 更快
  • 更稳定

里面保存的依然是:

1
2
3
4
layers.0.attn.q_proj.weight
layers.0.attn.k_proj.weight
layers.0.attn.v_proj.weight
...

这些 Tensor。

所以:

.safetensors 本质上依旧只是 Tensor 集合。


第二阶段:推理

真正开始出现区别的是这里。

很多人认为:

推理是不是就是 PyTorch Forward 一下?

其实不是。

例如:

Transformers:

1
2
3
4
5
6
7
8
9
10
11
12
13
Transformers



PyTorch



CUDA



GPU

但是:

很多推理框架根本不用 PyTorch。

例如:

1
llama.cpp

里面没有:

1
torch.nn.Linear

没有:

1
torch.matmul

甚至:

1
2
3
4
Attention
MatMul
KV Cache
Sampling

全部都是自己写的 C++ 实现。

因此:

它不能直接读取 PyTorch 的权重格式。


为什么需要 GGUF?

GGUF 可以理解成:

llama.cpp 专用模型格式。

GGUF 不只是保存权重。

它还保存:

  • Tokenizer
  • Vocabulary
  • Rope 参数
  • 特殊 Token
  • 模型配置
  • Metadata
  • Tensor 排布
  • 对齐方式
  • 量化方式

例如:

1
Qwen3-32B-Q4_K_M.gguf

实际上已经是:

一个完整模型。

不像 Hugging Face:

1
2
3
4
config.json
tokenizer.json
generation_config.json
model.safetensors

需要很多文件。

GGUF 一个文件全部解决。


GGUF 最大特点:已经量化好了

例如:

Hugging Face:

1
float16

可能:

1
2GB

转换以后:

1
Q4_K_M.gguf

可能:

1
500MB

为什么?

因为 Tensor 已经变成:

1
4bit

例如:

原来:

1
2
3
4
0.315
-0.102
0.443
...

float16。

转换以后:

1
2
3
01001010
11100011
...

已经不是 float16。

而是:

1
Q4_K_M

编码后的数据。

因此:

llama.cpp 加载的时候:

1
2
3
不用再量化

直接开始推理

为什么 Transformers 不能直接读取 GGUF?

原因很简单。

Transformers 期待:

1
float16 Tensor

例如:

1
torch.Tensor

但是:

GGUF 里面已经变成:

1
2
3
4
5
Q4_K_M

Q5_K_M

Q8_0

这些编码。

PyTorch 根本不知道如何解释。

因此:

1
2
3
4
5
6
7
8
9
GGUF



只能



llama.cpp

或者:

1
2
3
4
5
GGUF



Ollama

因为它们实现了对应的解码器。


Ollama 为什么可以一句话运行?

很多人觉得:

1
ollama run qwen3

非常神奇。

实际上:

底层就是:

1
2
3
4
5
6
7
8
9
Ollama



llama.cpp



GGUF

例如:

1
ollama run qwen3:32b

实际上就是:

1
2
3
4
5
6
7
8
9
下载 GGUF



llama.cpp 加载



开始推理

所以用户感觉非常简单。


为什么 vLLM 不支持 GGUF?

因为:

vLLM 和 llama.cpp 的目标完全不同。

llama.cpp 追求:

  • CPU
  • Mac
  • Metal
  • 边缘设备
  • 极低内存

而:

vLLM 追求:

  • GPU 最大吞吐
  • Tensor Parallel
  • Pipeline Parallel
  • Continuous Batching
  • Prefix Cache
  • PagedAttention

因此:

它更喜欢:

1
2
3
4
5
6
7
float16

bf16

fp8

int8

这些 GPU 可以高速计算的数据。

GGUF 更多是:

1
2
3
CPU 优化

Mac 优化

方向不同。


为什么不同框架不能互相读取?

可以想象:

Windows:

1
.exe

Linux:

1
ELF

Mac:

1
Mach-O

都是:

1
可执行程序

但是:

互相不能直接运行。

原因就是:

底层格式不同。

模型格式也是一样。

例如:

1
2
3
4
5
6
7
Transformers



期待:

float16 Tensor

而:

1
2
3
4
5
6
7
GGUF



期待:

Q4_K_M

因此:

必须使用不同加载器。


为什么很多模型都有多个版本?

例如:

Qwen3-32B

你可能看到:

1
2
3
4
5
6
7
8
9
Qwen3-32B



HuggingFace



model.safetensors

同时还有:

1
Qwen3-32B-Q4_K_M.gguf

还有:

1
TensorRT Engine

还有:

1
MLX

其实:

它们都是:

同一个模型。

只是:

采用了不同运行时。


各种格式之间是什么关系?

可以画成一张图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
                PyTorch 训练





safetensors / .pt



┌──────────────┼──────────────┐

▼ ▼ ▼

GGUF TensorRT CoreML

│ │ │

▼ ▼ ▼

llama.cpp TensorRT-LLM MLX
Ollama

所以:

真正重要的是:

所有格式都来源于同一份训练权重。

只是为了适应不同推理引擎。


常见模型格式总结

格式 主要用途 是否可继续训练 常见推理框架
.pt / .pth PyTorch 保存对象 PyTorch
.bin 早期 Hugging Face 权重 Transformers
.safetensors 当前 Hugging Face 主流格式 Transformers、vLLM、SGLang
.gguf llama.cpp 专用格式 ❌(通常不可直接训练) llama.cpp、Ollama、LM Studio
.onnx 跨框架推理格式 ONNX Runtime
.engine TensorRT 编译结果 TensorRT
.mlpackage Apple CoreML CoreML

底层真的都是 PyTorch 吗?

这是本文最重要的一点。

答案是:

训练阶段几乎都是 PyTorch。

但是:

推理阶段未必。

目前主流框架:

推理框架 是否依赖 PyTorch
Transformers ✅ 完全依赖
vLLM ✅ 模型定义依赖 PyTorch,但核心算子大量自定义 CUDA 实现
SGLang ✅ 基于 PyTorch + Triton 等优化
TensorRT-LLM ❌ 不依赖 PyTorch Runtime
llama.cpp ❌ 完全 C/C++ 实现
Ollama ❌ 底层基于 llama.cpp
MLX ❌ 使用 Apple MLX

因此:

很多推理框架甚至连:

1
torch.nn.Linear

都不会调用。

它们直接运行自己实现的:

  • MatMul
  • Attention
  • KV Cache
  • Sampling
  • CUDA Kernel
  • Metal Kernel

所以:

训练可以依赖 PyTorch,而推理完全可以摆脱 PyTorch。


总结

理解大模型权重格式,可以记住下面这张图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
               训练

PyTorch / JAX





.pt / .pth / .bin / safetensors



原始训练权重(可继续训练)



转换(Convert / Quantize)



┌───────────┼────────────┐

▼ ▼ ▼

GGUF TensorRT CoreML

▼ ▼ ▼

llama.cpp TensorRT Apple MLX
Ollama

一句话概括全文:

.pt.pth.bin.safetensors 更多是训练和通用权重格式;.gguf.engine.mlpackage 等则是针对特定推理引擎和硬件优化后的部署格式。模型本身没有变化,变化的是它的“打包方式”和“运行时”。真正决定模型能否运行的,不仅是权重文件,更是与之匹配的推理框架和底层执行引擎。