参数量、上下文窗口与模型规格怎么看
打开任何一个模型主页,迎面而来是一串像乱码的东西:
Qwen2.5-7B-Instruct-1M-Q4_K_M。每个片段都在告诉你一件具体的事,而这几件事合起来,正好决定了"我这台机器能不能跑"和"跑起来能聊多长"。这篇就是把这张卡片读穿。
本文的关系图(一个模型名,三步换算):
模型卡片 Qwen2.5 - 7B - Instruct - 1M - Q4_K_M
↓ ↓ ↓ ↓
参数量 对齐版本 上下文 量化档位
↓ ↓ ↓
权重占多少显存 KV Cache 多大 每个参数几字节
└────────────────┴───────┘
↓
一笔具体的账:显存够不够、能聊多长
一、先看一串让人头皮发麻的名字
Qwen2.5-7B-Instruct-1M-Q4_K_M
第一次看到大概会想:这什么玩意儿。
但把它拆开,信息量其实很规整------这串名字里没有一个字是装饰,每一段都在回答一个工程问题。
这篇就按名字从左到右走一遍。走完你会发现,所谓"模型规格"其实就是三笔账:
- 参数量(7B)→ 权重文件有多大,要多少显存才装得下;
- 上下文窗口(1M)→ 能一次读多长,以及为了这个"长"要付多少显存;
- 量化档位(Q4)→ 每个参数用几个字节存,直接决定前两笔账的数值。
这三笔账算清楚,选型基本就清楚了。
二、参数量:一个真的能数出来的东西
先说清楚"参数"是什么。
模型的本质是一堆矩阵 (可以想成一张张 Excel 表格)。训练的过程,就是反复微调这些表格里的每一个格子。表格里全部格子的总数,就是参数量。
所以 7B 的意思是:这个模型里一共有 76 亿个格子,每一个都存着一个小数。
这里先插一句救命的澄清:B 是 billion(十亿),不是 byte(字节)。 "7B 模型"说的是 70 亿个参数,不是 7 个 GB。这个误解每年要坑掉一批人的显存预算。
参数量怎么数?就是每个矩阵的行 × 列加起来。以一个真实模型为例,直接算给你看。
python
"""模型规格怎么读:把「7.6B / 128K / Q4」这些数字换算成显存。
以 Qwen2.5-7B 的真实结构参数为例,纯算术,只用标准库。
结论一句话:参数量决定「装得下吗」,KV Cache 决定「能聊多长」。
"""
L = 28 # 层数
D = 3584 # 隐藏维度
H = 28 # 注意力头数
KVH = 4 # KV 头数(GQA,4 个头共享)
HD = 128 # 每个头的维度
D_FF = 18944 # 前馈网络的中间维度
VOCAB = 152064 # 词表大小
CELL = 12
def disp_width(s):
"""显示宽度:中日韩字符占 2 格,用来对齐表格。"""
return sum(2 if ord(c) > 0x2E80 else 1 for c in s)
def pad(s, n, right=False):
fill = " " * max(0, n - disp_width(s))
return fill + s if right else s + fill
def row(cells, widths):
return "".join(pad(c, w, right=(i > 0)) for i, (c, w) in enumerate(zip(cells, widths)))
# ── ① 参数量是数出来的 ─────────────────────────────────────────
q_proj = D * D
k_proj = D * KVH * HD
v_proj = D * KVH * HD
o_proj = D * D
ffn = 3 * D * D_FF # SwiGLU:gate / up / down 三个矩阵
norms = 2 * D
per_layer = q_proj + k_proj + v_proj + o_proj + ffn + norms
embed = VOCAB * D
lm_head = VOCAB * D
total = per_layer * L + embed + lm_head + D
W = (30, 14)
print("① 7.6B 这个数字是怎么数出来的(单位:百万)")
for name, val in [("Q 投影 d×d", q_proj), ("K 投影 d×(kv*h)", k_proj),
("V 投影 d×(kv*h)", v_proj), ("O 投影 d×d", o_proj),
("FFN 三个矩阵", ffn), ("两个 LayerNorm", norms)]:
print(row([f" 每层 · {name}", f"{val/1e6:,.2f}"], W))
print(row([" 每层小计", f"{per_layer/1e6:,.2f}"], W))
print(row([f" × {L} 层", f"{per_layer*L/1e6:,.2f}"], W))
print(row([" 词表嵌入", f"{embed/1e6:,.2f}"], W))
print(row([" 输出层", f"{lm_head/1e6:,.2f}"], W))
print(row([" ---------------------------------------", "------------------------------"], W))
print(row([" 合计", f"{total/1e9:,.2f} B"], W))
print(f" 每层里 FFN 占 {ffn/per_layer:.1%}------参数量的大头是前馈网络,不是注意力")
print()
# ── ② 权重显存 ────────────────────────────────────────────────
print("② 权重显存 = 参数量 × 每个参数占几字节")
print(row([" 精度", "字节/参数", "权重显存"], (14, 12, 14)))
print(" " + "-" * 38)
for name, bpw in [("FP32", 4), ("BF16 / FP16", 2), ("INT8", 1), ("INT4", 0.5)]:
print(row([f" {name}", f"{bpw:g}", f"{total*bpw/1e9:.2f} GB"], (14, 12, 14)))
print()
# ── ③ KV Cache ────────────────────────────────────────────────
kv_per_token = 2 * L * KVH * HD * 2 # K 和 V,BF16 每元素 2 字节
print("③ KV Cache:每多一个 Token,都要多存一份它的 K 和 V")
print(f" 每个 Token:2(K/V) × {L}(层) × {KVH}(KV头) × {HD}(头维度) × 2 字节 = {kv_per_token:,} 字节")
print(row([" 上下文长度", "KV Cache 显存"], (14, 16)))
print(" " + "-" * 30)
for n, label in [(4096, "4K"), (32768, "32K"), (131072, "128K")]:
print(row([f" {label}", f"{n*kv_per_token/1e9:.2f} GB"], (14, 16)))
print(" 注意:这是「一条对话」的量。并发 10 个用户,就再乘 10。")
print()
# ── ④ GQA 省了多少 ────────────────────────────────────────────
kv_mha = 2 * L * H * HD * 2
print("④ GQA 到底省了多少:如果 KV 头也用 28 个(老式 MHA)")
print(row([" 结构", "每 Token", "128K 上下文"], (20, 14, 16)))
print(" " + "-" * 50)
print(row([" MHA(KV 头 28)", f"{kv_mha:,}", f"{131072*kv_mha/1e9:.2f} GB"], (20, 14, 16)))
print(row([" GQA(KV 头 4)", f"{kv_per_token:,}", f"{131072*kv_per_token/1e9:.2f} GB"], (20, 14, 16)))
print(f" 只改这一处,KV Cache 缩小到 1/{kv_mha//kv_per_token}。")
print()
# ── ⑤ 一张 8GB 显卡的账本 ─────────────────────────────────────
print("⑤ 一张 8GB 显卡,实际能开多长上下文")
overhead = 1.0e9
free = 8e9 - total * 0.5 - overhead
print(row([" 权重(INT4)", f"{total*0.5/1e9:.2f} GB"], W))
print(row([" 框架 / CUDA 开销", f"{overhead/1e9:.2f} GB"], W))
print(row([" 留给 KV Cache", f"{free/1e9:.2f} GB"], W))
print(f" → 能撑约 {free/kv_per_token/1024:.0f}K 上下文(当前 GQA 结构)")
print(f" → 若换成不支持 GQA 的老结构,只剩 {free/kv_mha/1024:.0f}K")
print()
print(" 所以:能不能跑起来看权重,能聊多长看 KV Cache。两笔账是分开的。")
真实运行结果 (本机实测,完整脚本见 code/06-model-spec-demo.py):
① 7.6B 这个数字是怎么数出来的(单位:百万)
每层 · Q 投影 d×d 12.85
每层 · K 投影 d×(kv*h) 1.84
每层 · V 投影 d×(kv*h) 1.84
每层 · O 投影 d×d 12.85
每层 · FFN 三个矩阵 203.69
每层 · 两个 LayerNorm 0.01
每层小计 233.05
× 28 层 6,525.49
词表嵌入 545.00
输出层 545.00
--------------------------------------- ------------------------------
合计 7.62 B
每层里 FFN 占 87.4%------参数量的大头是前馈网络,不是注意力
② 权重显存 = 参数量 × 每个参数占几字节
精度 字节/参数 权重显存
--------------------------------------
FP32 4 30.46 GB
BF16 / FP16 2 15.23 GB
INT8 1 7.62 GB
INT4 0.5 3.81 GB
③ KV Cache:每多一个 Token,都要多存一份它的 K 和 V
每个 Token:2(K/V) × 28(层) × 4(KV头) × 128(头维度) × 2 字节 = 57,344 字节
上下文长度 KV Cache 显存
------------------------------
4K 0.23 GB
32K 1.88 GB
128K 7.52 GB
注意:这是「一条对话」的量。并发 10 个用户,就再乘 10。
④ GQA 到底省了多少:如果 KV 头也用 28 个(老式 MHA)
结构 每 Token 128K 上下文
--------------------------------------------------
MHA(KV 头 28) 401,408 52.61 GB
GQA(KV 头 4) 57,344 7.52 GB
只改这一处,KV Cache 缩小到 1/7。
⑤ 一张 8GB 显卡,实际能开多长上下文
权重(INT4) 3.81 GB
框架 / CUDA 开销 1.00 GB
留给 KV Cache 3.19 GB
→ 能撑约 54K 上下文(当前 GQA 结构)
→ 若换成不支持 GQA 的老结构,只剩 8K
所以:能不能跑起来看权重,能聊多长看 KV Cache。两笔账是分开的。
① 这张表最值得注意的不是总数,而是最后那行:FFN 占了每层参数的 87.4%。
很多人直觉上以为参数量主要在"注意力"上------恰恰相反。注意力负责在词之间搬运信息 ,但它本身没那么大;真正堆满参数的是那一层层的前馈网络。所以你听到"70B 参数",其中大头是几十层叠起来的小型全连接网络。
还有一个细节:K、V 投影只有 1.84M,远小于 Q 和 O 的 12.85M------因为这里用了 GQA ,28 个注意力头共享 4 组 K、V。这是为了省显存专门设计的,第六节会算它省了多少。
三、参数量决定了什么,又不决定什么
它决定的:
- 权重文件多大------一个参数占几字节,乘一下就是文件大小;
- 最少要多少显存------装不下权重,一切免谈;
- 大致多慢------参数量越大,每生成一个 Token 要翻的"格子"越多。
它不决定的:
参数量不等于知识量。 有个流传挺广的估算:模型存储事实性知识的上限,大约是每个参数 2 bit 。按这个算,一个 7B 模型全部参数用来"背事实",理论上限是 150 亿比特左右------折合不到 2 GB 的纯文本。
这个数字看着小得离谱,但它说明了一件事:模型不是靠"存"才显得知道得多的,它是靠"压缩"和"泛化"。同一个事实,它用一种比原文抽象得多的方式记着,所以才能举一反三。
参数量更不等于聪明。 同样 7B,不同数据配比、不同训练方法,出来的效果能差出一整个档次。参数量只是"容量",容量大不等于装得好。
这里有个值得记住的经验比例:在给定算力预算时,参数量和数据量要按比例增长------大致上每个参数配 20 个左右的 Token 是比较划算的配比。太少,参数浪费(模型"喂不饱");太多,参数不够装(模型"记不住")。
但今天的实际情况是故意打破这个比例 :参数压小、数据喂爆 。为什么?因为训练是一次性成本,推理是天天要付的钱 。把模型训小一点、多训几十倍数据,效果能追上来,但推理成本直接降一个数量级。这笔账在商业上完胜。
四、最容易踩的一个坑:MoE 的"两个参数量"
现在买模型,你会看到一种吓人的规格:
总参数 671B,激活参数 37B
一个 671B 的模型?那不是要 8 张 H100 起步?
先别慌。这是 MoE(混合专家) 结构,它有两个参数量,而且必须分开看:
- 总参数 :模型里所有格子的总数------671B。显存要按这个数准备。
- 激活参数 :处理每一个 Token 时,实际参与计算 的那部分------37B。速度按这个数估。
MoE 的做法是把前馈网络拆成很多个"专家",每个 Token 只挑其中几个用。结果就是:这个模型囤了 671B 的知识,但每次只动用 37B 的算力。
带来的两个后果,一定要记住:
好消息:它比同等总参数的稠密模型快得多。 一个 671B 的 MoE,跑起来的速度可能接近一个 40B 的稠密模型。
坏消息:它对显存极其不友好。 因为专家是随 Token 挑的,你没法提前知道这次要用哪几个 ,所以全部专家的权重都得常驻在显存里。省了算力,省不了显存。
所以看到 MoE 规格,两句话要同时写在心里:"跑得动吗"看总参数,"跑得快吗"看激活参数。 只盯着其中一个数,判断一定会错。
五、上下文窗口:那个数字的真相
上下文窗口,就是模型一次最多能"看见"多少 Token。
这个数现在越标越大:4K、32K、128K、1M。但有四件事,宣传页上通常不会写。
第一,单位是 Token,不是字。
"支持 128K"指的是 128K 个 Token。中文大致一个汉字一个 Token 上下,但这个比例随分词器差异很大------同一个窗口,能装的中文和能装的英文完全不是一回事。
第二,标称 ≠ 有效。
一个很稳定的实验现象:把关键信息放在长文本的中间,模型最容易漏掉 ;放在开头或结尾,反而找得到。这就是所谓"中间迷失"。所以"支持 128K"不等于"128K 里的每个字都读进去了"。 真要用长上下文,把重要的东西放头尾,或者干脆多问一遍。
第三,长上下文不是免费的。
这是本文要算的重点。窗口每翻一倍,KV Cache 就翻一倍 ,而注意力的计算量翻 4 倍。128K 听起来很美,看到第六节的账单你就知道它有多贵。
第四,最关键的一条:上下文不是记忆。
窗口里的内容,对话一结束就没了。它是"临时工作台",不是"硬盘"。 想让它真的记住,得靠检索、向量库、微调那一整套外挂手段------塞进上下文里,只是"这次它能看见",不是"它学会了"。
六、动手算:规格 → 显存
现在把 ②③④⑤ 四张表连起来读,这才是这篇的正文。
先看 ②:权重显存。
同一个模型,换个精度,显存需求差 8 倍。FP32 要 30 GB,INT4 只要 3.8 GB。这就是量化存在的全部理由。
再看 ③:KV Cache。
这是全篇最容易被忽略的一笔账。
生成是一个 Token 一个 Token 吐 的。每吐一个新 Token,都要拿它的 Q 去跟前面所有 Token 的 K、V 算一遍。如果前面那些 K、V 不存下来,每走一步都得把历史重算一遍------那就是把 O(n) 的事干成了 O(n²)。所以必须缓存。
但缓存是显存里实实在在的一坨,而且它随上下文线性增长:
- 4K 上下文:0.23 GB,无所谓;
- 32K:1.88 GB,开始有感觉了;
- 128K:7.52 GB------差不多是这个模型 INT4 权重的两倍。
再加上那句提醒:这是"一条对话"的量。 并发 10 个用户,乘 10。所以线上服务的显存瓶颈,经常不是模型权重,而是 KV Cache。 你看到"加大显存"的账单,一大半在为这个付钱。
然后是 ④:GQA 省了多少。
如果按老式做法让 28 个注意力头各自拥有自己的 K、V,128K 上下文要 52.61 GB------单是缓存就把一张 80G 的卡吃掉三分之二。
改成 4 个头共享一组 K、V(这就是 GQA),同样的上下文只要 7.52 GB ,缩小到 1/7。
这是现代模型几乎都上 GQA 的原因:它不改变表达能力多少,但把这份最"虚"的开销砍到了零头。
最后是 ⑤:一张 8GB 显卡的完整账本。
权重(INT4) 3.81 GB
框架 / CUDA 开销 1.00 GB
留给 KV Cache 3.19 GB → 约 54K 上下文
这张表解释了一个非常常见的困惑:
"我 8G 显卡,7B 模型明明能跑起来,为什么一开长上下文就崩?"
因为跑得起来和跑得久,是两笔账。3.81 GB 的权重只是"入场券"。你还能聊多长,取决于剩下那 3.19 GB 能装下多少 KV Cache------而这个值在 GQA 的帮助下才勉强到 54K,换成没有 GQA 的结构只剩 8K。
所以选型时,两个数必须一起看:参数量决定"装得下吗",KV Cache 决定"能聊多长"。
七、量化:Q4 到底损了什么
回到名字里最后一段:Q4_K_M。
量化就是:把每个参数从"占 4 字节"压缩成"占 0.5 字节"。
| 精度 | 每个参数 | 含义 | 7B 模型权重 |
|---|---|---|---|
| FP32 | 4 字节 | 单精度浮点,训练用 | 30.46 GB |
| BF16 / FP16 | 2 字节 | 半精度,推理默认 | 15.23 GB |
| INT8 | 1 字节 | 8 位整数 | 7.62 GB |
| INT4 | 0.5 字节 | 4 位整数,消费级显卡的救星 | 3.81 GB |
你在下载页上见到的那堆后缀,基本都在说这一件事:
- GGUF :一种文件格式,专为本地推理设计,常配
Q4_K_M这样的档位名------Q4是 4 位,_K_M是它内部的分组策略(哪些层量化得狠一点、哪些留得精细一点); - AWQ / GPTQ:两种常见的量化算法,思路都是"按重要性区别对待"------对结果影响大的权重保留更高精度,不重要的压得更狠;
- bitsandbytes:HuggingFace 生态里最省事的一种,加载时在线量化。
关键问题:量化会不会让模型变笨?
诚实回答:会,但通常是"变钝"而不是"变傻"。
日常问答、写文案、翻译这类任务,Q4 和 FP16 的差别往往察觉不到。但有两类能力先受影响:
- 需要精细分辨的判断("这两个有什么区别");
- 长链条推理(多步数学、复杂逻辑)。
原因也不玄:量化本质上是在抹掉小数位。模型内部有些判断依赖的正是那些微小的数值差异,抹平了就分辨不出来了。
一条来自实践的粗略经验:4 bit 通常是性价比的拐点。 从 16 bit 压到 4 bit,显存省了 3/4,效果掉得很少;但继续往 3 bit、2 bit 压,掉速会突然变陡 。所以在没有实测数据的情况下,Q4 是默认安全的选择,除非你的任务特别吃推理精度。
顺便纠正一个常见期待:量化主要是省显存和显存带宽,不保证加速。 省了显存意味着能跑更大的模型、更长的上下文,这本身是收益;但计算本身未必更快,有时甚至因为要在计算时反量化而略慢。
八、现在回去读那串名字
把每一段对号入座:
Qwen2.5 - 7B - Instruct - 1M - Q4_K_M .gguf
↓ ↓ ↓ ↓ ↓ ↓
系列/代际 参数量 对齐版本 上下文 量化档位 文件格式
↓ ↓ ↓ ↓ ↓
越新越好 决定权重 是否经过 决定 KV 决定权重
文件大小 指令微调 Cache 上限 实际大小
几个容易忽略的点:
Instruct/Chat和Base是两个东西。Base是原始模型,只会"接着往下写",不会听指令;Instruct是经过指令微调和人类偏好对齐的版本,才是你能直接对话的那个。你下载错了版本,会觉得这模型怎么这么蠢------因为它压根没被教过"回答问题"这件事。- 同一代会有多个尺寸 (0.5B / 1.5B / 7B / 14B / 32B / 72B)。它们不是"同一个模型的不同压缩版",而是各自独立训练的。 小尺寸版本的能力往往是断崖式下降的,不是平滑缩水。
- "1M"这种超长上下文,常常是单独训练的变体 ,不是所有尺寸都有。同一个模型家族里,长上下文版和平常版可能是两套不同的权重。
九、四个常见误区
误区一:以为 7B 里的 B 是字节。
是 billion(十亿个参数)。这个误解的后果很直接:以为 7B 模型文件是 7 GB,结果一算 FP16 要 15 GB,显卡当场不够。
误区二:以为参数量大就更强。
参数量是容量 ,不是水平 。数据配比、训练轮数、对齐质量,每一样都能把参数量带来的优势吃掉。跨代比较更是陷阱:新一代的 7B 打赢上一代的 13B,是常态。
误区三:以为标称上下文就是可用上下文。
128K 是"窗口物理上限",不是"它真能读透 128K"。中间的信息容易被漏,而且越长的上下文,单位 Token 的推理成本越高。 需要长上下文时,建议实测一下:把答案埋在中间,看它找不找得到。
误区四:以为显存够装权重就能跑。
这只是入场券 。KV Cache、框架开销、并发用户的份,全都要算进去。算显存只算权重,是新手最常见的翻车方式。
十、把这件事放回地图上
把大模型的概念按依赖关系从下往上摞成几层------最底下是算力和数据,往上依次是文字的表示、模型结构、训练、调用,最顶上才是增强和智能体:
L6 智能体层 Agent / Function Calling / ReAct / 记忆 / MCP
L5 增强层 RAG / 向量检索 / Rerank / GraphRAG / 微调
L4 调用层 Prompt / Few-shot / CoT / 温度 / Top-p
L3 训练层 预训练 / SFT / RLHF / DPO / LoRA / Scaling Law
L2 结构层 ★ Transformer / 注意力 / 参数量 / 上下文窗口 ★ ← 本篇在这里
L1 表示层 Token / 分词 / 词表 / Embedding
L0 底座 算力 / 数据 / 成本
本篇讲的和结构层是同一层------但视角不同:那一层讲的是"模型内部长什么样",这一篇讲的是"这些结构怎么变成你账单上的数字"。
三个数往回传的链条特别清楚:
- 参数量往上决定了训练要多少算力、推理要多少显存;
- 上下文窗口直接卡死了 RAG 能塞多少资料、智能体能带多少历史;
- 量化档位决定了这一切能不能在一块消费级显卡上跑起来。
所以规格不是"参数表",是预算表。 你在 L4 写提示词、在 L5 搭检索、在 L6 做智能体,天花板全是被 L2 这三个数定下来的。
这一层留下的窟窿也很明确:上下文再长也买不来"记忆",窗口再大也不等于"读懂"。 既然塞进窗口的内容既有限、又会被漏掉、还会随对话消失------那就干脆换个思路:把知识放在模型外面,用的时候现查。 这个思路后来长成了一整套体系。
三条可以带走的结论
- B 是十亿,不是字节。参数量 = 权重文件大小的分子,也是显存门槛的最小值。它是容量,不是水平。
- 显存有两笔账:权重决定"装得下吗",KV Cache 决定"能聊多长"。上下文越长,第二笔账涨得越快------这是 GQA 存在的理由。
- 量化是显存和精度之间的旋钮。4 bit 通常是性价比拐点;它换来的是"跑得动更大的模型",不保证更快,也不保证不伤推理精度。
《大模型概念地图》专栏 · 第 6 篇