GGUF 量化
权重(Weights)
神经网络里,权重是模型在训练中学到的数字参数。它们决定"输入怎么变成输出"。
可以把它想成一张巨大的调节表:
- 每个神经元和下一层神经元之间,都有一条连接
- 每条连接上有一个数字(权重)
- 推理时:输入 × 权重 → 累加 → 激活 → 下一层
例如一个 7B 模型,大约有 70 亿个这样的数字。模型文件里存的,主要就是这些权重(外加少量结构、词表等元数据)。
存储精度基础
32 bit 是什么意思?
bit(比特) 是计算机里最小的存储单位,只能是 0 或 1。
32 bit 表示:每个权重用 32 个二进制位 存,也就是 4 字节。
决定了能存多大的数",还决定了 小数能精细到什么程度
"约 7 位有效数字"是什么意思
有效数字:从左边第一个非 0 数字开始,到最右边那位,都算有效数字(中间的 0 也算)。
什么是量化(Quantization)?
量化是把模型权重从高精度浮点数压缩为低精度整数的过程:
| 格式 | 每个权重占用 | 说明 |
|---|---|---|
| F32 | 32 bit | 完整精度,最大最慢 |
| F16 | 16 bit | 半精度,Ollama 默认 |
| Q8_0 | 8 bit | 轻微损失,接近原始 |
| Q6_K | 6 bit | 极小损失,推荐 |
| Q4_K_M | 4 bit | 小损失,最流行(速度/质量平衡) |
| Q3_K_M | 3 bit | 明显损失,极小内存 |
| Q2_K | 2 bit | 大幅损失,极端压缩 |
举例:一个 7B 参数模型:
- F16:~14 GB
- Q4_K_M:~4 GB(缩小到原来的 28%)
- Q2_K:~2.7 GB
精度损失(Precision Loss)
精度指每个权重用多少 bit、以什么数值格式存储。
| 格式 | 每个权重 | 能表达的数值 |
|---|---|---|
| F32 | 32 bit | 约 7 位有效数字 |
| F16 | 16 bit | 约 3~4 位有效数字 |
| Q4 | 4 bit | 只有 16 个离散档位 |
精度损失就是:把高精度数字压成低精度后,每个权重不再等于原来的精确值,而是被"四舍五入"到更粗的档位上。
类比:
- F32:用尺子量到 0.01 mm
- Q4:只能标到 0、1、2...15 共 16 档
对大多数权重,这种误差很小;但对少数关键权重,误差会稍大。K-quant(如 Q4_K_M)的做法是:重要层用更高精度,次要层用更低精度,在体积和质量之间折中。
什么是 GGUF?
GGUF(GPT-Generated Unified Format)是 llama.cpp 项目定义的模型文件格式,用于存储量化后的模型。Ollama 底层就是用 GGUF 来存储和运行模型的。
一个 .gguf 文件包含:
- 模型权重(已量化)
- Tokenizer 词表
- 模型结构元数据
Ollama 中的量化命名规则
qwen2.5:7b-instruct-q4_K_M
^^^^^^^^^^^^^^^^^^
quantization tag
- Q4 = 4 bit
- K = K-quant(更智能的混合量化)
- M = medium(关键层用更高精度)
如果只写 qwen2.5:1.5b,Ollama 默认选择 Q4_K_M。
Python 实践
subprocess.run 获取模型的量化信息
在 Python 里调用一条终端命令(ollama show)
py
import subprocess
result = subprocess.run(
["ollama", "show", "qwen2.5:1.5b"], # 命令拆成列表
capture_output=True, # 捕获 stdout 和 stderr
text=True, # 输出以字符串返回(而非 bytes)
encoding="utf-8",
)
print(result.stdout) # 命令的标准输出
result.stdout 是一个多行字符串,每行格式类似:
quantization Q4_K_M
parameters 1.5B
可以用 .splitlines() 逐行遍历,再用 .startswith() 找到你需要的字段
调用本地模型
py
def run_model(model_name: str, prompt: str) -> str:
"""调用模型,返回 (回复内容, 耗时秒数)"""
llm = ChatOllama(model=model_name, client_kwargs={"trust_env": False})
t0 = datetime.now()
messages = [HumanMessage(content=prompt)]
result = llm.invoke(messages)
elapsed = (datetime.now() - t0).total_seconds()
print(f"Model: {model_name}, Time: {elapsed:.2f}s")
return result.content