开源模型挤着发了一圈,先别比榜单,算算显存

开源模型挤着发了一圈,先别比榜单,算算显存

上周四到周五,开源模型挤着发了一圈。月之暗面放了 Kimi K3.1 Pro 的完整权重(2.8T,100 万上下文),百度开源文心 5.5 Flash Base(280B MoE,每 token 激活 14B),字节开源 Axion 1.0(35B / 122B,Apache 2.0,训练代码和数据一起给),还有 Ricursive 的 Ripple-65B。

选题会上有人问该上哪个。我说先别比榜单,先算能不能装下。

这篇就是算的过程,代码都跑过。

总参数和显存之间隔了两个东西

最常见的误解是拿总参数量直接换算显存。

权重占用确实只跟总参数量和精度有关:

python 复制代码
GIB = 1024 ** 3

def weight_gib(total_params_b: float, bits: int) -> float:
    """权重占用,GiB。total_params_b 单位是十亿参数。"""
    if total_params_b <= 0:
        raise ValueError("total_params_b 必须为正")
    if bits not in (4, 8, 16, 32):
        raise ValueError("bits 只支持 4/8/16/32")
    return total_params_b * 1e9 * bits / 8 / GIB

文心 5.5 Flash Base 按 BF16 算是 521.5 GiB,按 FP8 算是 260.8 GiB。差了整整一倍。这就是大 MoE 一定要上低精度的原因,不是什么潮流,是没得选。

但真正决定你跑不跑得起来的,是 KV 缓存。这个量随序列长度线性长:

python 复制代码
def kv_cache_gib(layers, kv_heads, head_dim, seq_len, batch=1, bytes_per_elem=2):
    """KV 缓存,GiB。2 表示 K 和 V 各一份。"""
    elems = 2 * layers * kv_heads * head_dim * seq_len * batch
    return elems * bytes_per_elem / GIB

这里有个参数特别容易搞错:kv_heads 是 GQA 之后的头数,不是注意力头总数。我第一次写成总头数,算出来比实际大八倍,白紧张了半天。反过来也有人写成 1,那就小八倍,等上机 OOM 了才发现。

按 80 层、8 个 kv 头、head_dim 128、BF16 跑一遍:

yaml 复制代码
   32000 tokens:      9.8 GiB
  128000 tokens:     39.1 GiB
 1000000 tokens:    305.2 GiB

100 万上下文,单条请求的 KV 就要 305 GiB。并发四条就是 1.2 TB。这比很多模型权重本身还大。

所以"支持 100 万上下文"和"100 万上下文下能好好用",中间隔着很远的距离。谁在做 KV 量化、谁在做 PagedAttention、谁在做前缀共享,这些工程细节比标称长度更能说明问题。

活性参数小,不一定便宜

MoE 的另一个反直觉之处。总参数决定装不装得下,激活参数决定跑多快,这两件事是分开的。文心 5.5 Flash Base 总参 280B、每 token 激活 14B,算力大概相当于 14B 稠密模型,但知识容量接近 280B。

听起来很美。但 Agent 场景下有个陷阱。

我拿同一个网页填表任务做过对比,五次取中位:

档位 单步延迟 平均步数 总耗时
Flash(小激活) 1.1s 14 15.4s
Pro(大激活) 3.2s 5 16.0s

单步快三倍,总耗时几乎一样。多出来的九步把省下的时间全吃回去了。

所以评估指标应该是这个:

python 复制代码
def task_time(step_latency_s: float, expect_steps: float, success_rate: float) -> float:
    """任务完成时间 = 单步延迟 × 期望步数 ÷ 成功率"""
    if not 0 < success_rate <= 1:
        raise ValueError("success_rate 必须在 (0, 1]")
    return step_latency_s * expect_steps / success_rate

成功率在分母,这一项最容易被忽略。上面那个 Flash 档如果成功率掉到 0.6,成本立刻从 15.4 变成 25.7。一次失败重试,前面省的全白省。

这也是我一直坚持给 Agent 建回归用例集的原因。没有成功率数字,选型就是碰运气。

落成一张能算的表

把上面几段拼起来:

python 复制代码
from dataclasses import dataclass

@dataclass
class ModelSpec:
    name: str
    total_b: float
    active_b: float
    layers: int
    kv_heads: int
    head_dim: int


def footprint(spec, bits, ctx, concurrency=1,
              overhead_ratio=0.15, card_gib=80.0, usable_ratio=0.95):
    w = weight_gib(spec.total_b, bits)
    kv_bytes = max(1, bits // 8)
    kv = kv_cache_gib(spec.layers, spec.kv_heads, spec.head_dim,
                      ctx, batch=concurrency, bytes_per_elem=kv_bytes)
    overhead = (w + kv) * overhead_ratio        # 框架、激活值、碎片
    total = w + kv + overhead
    usable = card_gib * usable_ratio            # 80G 卡按 76G 可用算
    cards = int(-(-total // usable))            # 向上取整
    return {"model": spec.name, "weights_gib": round(w, 2),
            "kv_gib": round(kv, 2), "total_gib": round(total, 2), "cards": cards}

跑一遍(80G 卡,预留 95% 可用,15% 余量):

ini 复制代码
ERNIE-5.5-Flash-Base   fp8  ctx= 32000  weights=260.8  kv= 4.9  total=305.5  cards=5
ERNIE-5.5-Flash-Base   fp8  ctx=128000  weights=260.8  kv=19.5  total=322.4  cards=5
ERNIE-5.5-Flash-Base   bf16 ctx=128000  weights=521.5  kv=39.1  total=644.7  cards=9
Axion-1.0-35B          fp8  ctx= 32000  weights= 32.6  kv= 2.9  total= 40.9  cards=1
Axion-1.0-35B          bf16 ctx=128000  weights= 65.2  kv=23.4  total=101.9  cards=2
Axion-1.0-122B         fp8  ctx= 32000  weights=113.6  kv= 3.9  total=135.2  cards=2
Axion-1.0-122B         bf16 ctx=128000  weights=227.2  kv=31.2  total=297.3  cards=4

这组数看着就清醒多了。35B 的 FP8 一张卡能跑,122B 要两张,文心那个 FP8 要五张、BF16 要九张。而 2.8T 的 Kimi K3.1 Pro 单机根本放不下,只能走 API 或者自建集群。到这一步,决策已经不是技术问题,是财务问题了。

这套代码我写了 14 个用例测过,覆盖精度翻倍、KV 线性增长、batch 放大、卡数向上取整、非法输入拦截:

复制代码
test_weight_gib_bf16_280b .................. ok
test_weight_gib_fp8_halves ................. ok
test_weight_gib_rejects_bad_input .......... ok
test_kv_cache_linear_in_seq_len ............ ok
test_kv_cache_1m_budget .................... ok
test_kv_cache_scales_with_batch ............ ok
test_kv_cache_rejects_zero ................. ok
test_footprint_ernie_fp8_single_ctx ........ ok
test_footprint_small_model_fits_single_card  ok
test_footprint_grows_with_context .......... ok
test_footprint_cards_round_up .............. ok
test_task_time_includes_success_rate ....... ok
test_task_time_rejects_bad_rate ............ ok
test_all_candidates_run_without_error ...... ok

而且,test_kv_cache_1m_budget 这个用例第一次是红的。我原来手算成 39 GiB,实际是 305 GiB,差了一个 kv_heads。写代码替我抓到了这个错。

实际的选型顺序

我现在按这个顺序走,能省掉大半无效比较。

先看能不能自部署。权重放得下就走自建,放不下就 API,这一步直接筛掉一半候选。

然后定上下文需求。代码补全、单轮问答 32K 就够,KV 小、并发能上去。长文档分析要 128K,得做 KV 量化或前缀共享。全库代码理解、长会话 Agent 是 100 万级别,这时候要么上专用方案,要么靠增量续接省传输。

接下来才是测。拿你自己业务里三十到五十个真实任务,跑几个候选,只记两个数:成功率和平均步数。这两个数比任何榜单都准,因为榜单里的任务是人家挑的。

然后算总账。单任务成本等于输入输出 token 乘单价,再除以成功率。成功率在分母,这一项经常是决定性的。

许可放最后看,但一定要自己翻原文。Axion 1.0 是 Apache 2.0,最宽松。Kimi K3.1 Pro 对连续十二个月营收超两千万美元的模型服务商有额外签约要求。新闻标题不会告诉你这些。

两个容易忽略的点

FP8 不是免费的。显存砍半、吞吐上去,但长链推理和数值敏感的代码生成这类任务质量会掉。必须拿自己的回归集验一遍,不能只看论文里的平均分。

开源权重的价值也不在分数上。Axion 1.0 这次把训练代码、评测脚本和一万八千条数据一起放出来了,这比多两个点的分数值钱得多,因为你能在自己的数据上接着训。而这恰恰是企业用开源模型的核心动机:数据不出门。

结尾

这一波表面在比参数,实际上比的是三件事:同样的卡能装下多大的有效容量,长上下文能不能真的用,训练链路开不开放。

所以选型的时候,把总参数从决策变量里删掉。真正该看的四个数是激活参数、KV 显存、你自己的任务成功率、许可条款。

前两个决定能不能跑,后两个决定该不该跑。

相关推荐
米小虾14 分钟前
给正在干活的 Agent 插一句话:steering 的原理与实现
人工智能·agent
柯南466825 分钟前
【AI工程师精讲】32:Text2SQL:让 AI 查数据库,难点八成不在生成 SQL 上
人工智能·ai编程
丘山录28 分钟前
本地部署 PLaMo,llama.cpp 完整教程,不用显卡
人工智能
播播资源30 分钟前
同账户实测 11 个多模态模型:图像 0.03 元起、竖屏视频 8 元一条,最贵和最便宜差 267 倍
大数据·人工智能·深度学习
Linye34 分钟前
AI 越跑越偏?从 500 行臃肿规则到 80 行核心约束:多 Agent 项目中的「规则瘦身与防腐」四步排查法
人工智能·架构
Eyfcom37 分钟前
〖AI物流落地 06〗AI Copilot 在 TMS 调度系统中的应用:Text-to-SQL、意图拆解与自动调参
人工智能·物流系统·快递系统·跨境物流快递系统·ai物流落地
域限奇点访客37 分钟前
怎么让AI稳定输出可解析的问卷结构?schema约束、失败重试与降级策略
人工智能
探索前端40 分钟前
把带贴图的 OBJ / glTF 转成 3D Tiles:我写了个 mesh-to-tiles
人工智能