本地大模型之路(二):了解模型能力与性能需求,让硬件选购恰到好处
上一篇文章我们主要讲了为什么选择以及选什么本地大模型。但是选择什么模型其实受到设备的制约,如果你还没有入手一台合适的设备,那么反过来还可以通过需求出发帮助选购合适的产品。
首先需要声明的是:
我们都知道当今的语言大模型实质上的架构都基本一致,是仅有解码器的 Transformer+(加号和原始论文结构做区分)。如果你在网上搜什么是 Transformers,很可能会搜到 Jay Alammar 这篇著名的博客1。假如还是有点模糊,可以看看这几个链接了解基本概念2、数学原理3及其可视化4。对用户来说这些细节能帮助我们更深入了解内部原理,但是并不重要,本文会尽量简化不必要的细节。
大模型本质上是一种特殊的程序:输入指令,往后吐字。模型并不能直接处理文字,现在比较通用的做法是使用 tokenizer 把文本映射到词表中的坐标——可以想象一本《新华字典》,在里面的所有的词或字用从 0 开始的序号标记,模型编码的时候就是从字典里面读这个的标号,解码的时候反之。下图我们简化一下,每个 token 对应一个字:

注意一般情况下我们不会只选取概率最大的 token 进行输出,这样这个程序的输出变成完全确定,并不利于创意类生成,所以需要额外的参数(temperature, topk 等等)来加入一些采样随机性。
我们可以区分实际计算的两个过程:
实际我们在使用流式的大模型应用时,输入请求之后会等一会儿,模型开始出第一个词然后继续输出,也就是对应这两个状态。
我们继续放大,看模型内部。

基于 Transformers 的语言模型有着非常规整的结构——除了输入输出就是层层叠叠重复的 Transformer 块。正如它的名字的含义,对词向量不停地做计算,「变换」其表示。
每个模型的架构确定,则每一步的计算公式都确定了,例如线性的 f(x)=ax+b,而模型的权重或者参数即其中的 a 和 b。
实际上 LLM 的参数量和计算量都要大非常多,以 10B 参数为例,一共有 10^10 个参数,如果都用半精度(FP16/BF16)存储,大小为 10^10×16/8 bytes = 20 GB。
有了大致的模型概念,我们可以想象一下模型在做前向推理的时候,实际在执行的操作,以 CPU 推理为例:

如果以大多数 M 系列芯片的 Mac mini 基础款为例,从 SSD 加载模型的速度大概是 3 GB/s,因此为了提高推理速度,更高速的内存自然是必须的。接下来从内存中加载参数到计算核心的 SRAM,这个速度则和内存带宽有关系,频率更高的 DDR5 内存肯定比 DDR4 更快。但是 DDR5 内存带宽相对于计算更快的 GPU 就不够了,比如算力更加强劲的 NVIDIA 卡,如果显存带宽不够,计算核心算完了只能干等着数据搬过来,影响整体推理效率。举个例子,两个人玩《胡闹厨房》,一个人负责搬运食材,另一个人负责做菜,尽管做菜速度飞快,但是搬食材的慢了则流水线的效率也上不去。因此一些更加专业的训练卡,比如 A100 会配备更高速的 HBM2 显存,能达到 1555 GB/s 的显存带宽5。
因此也延伸出了两个问题:
前面我们提到一个 10B 的模型,半精度下权重就有 20 GB,轻轻松松就超过了丐版 Mac mini 的 16 GB。除了权重之外,随着序列增长的 KV Cache 以及计算的中间激活同样存在于内存中。我们当务之急是解决这个问题。
使用更大的内存?最大的 GeForce RTX 40 系显卡也只给了 24 GB,连 Qwen2.5-14B 都装不下;M4 Mac mini 如果要加到 24GB 需要加 3000 人民币。虽然说加钱世界可及,如果我们不想加钱呢?
在 LLM 的计算中,大多数计算在半精度下执行。我们先了解下半精度浮点数的表示方法:如图6所示,以 FP16 为例,第一位为符号位(s),紧接着 5 位为指数位(e),剩下的为尾数位(m),当指数位不等于 (00000)₂ 或者 (11111)₂ 时,表示的值为 (-1)ˢ×2ᵉ×1. (m)₂。比如 0 01101 0101010101 可以表示 2⁻² × (1 + 341/1024 ) ≈ 0.33325195。
如果我们只想用 8 位来存储权重呢?如果按照同样的浮点数表示方式,势必需要缩减指数位和尾数位。对应减少了动态范围和精度,比如英伟达在 H100 中加入了 FP8 的支持7,分为两种数值类型:
nan, 精度比 E5M2 高nan 和 +/- inf,表示范围比 E4M3 大
虽然动态范围和精度比半精度有进一步下降,但是经过合理的配置甚至是可以正常训练的。可是如果我们的硬件并不支持 FP8,以及想进一步压缩每个权重的表示位数,有没有更通用的办法呢?
最广泛被采用的就是整型,比如 INT8/INT4。但是 INT8 只能表示 -128 到 127 的整数,我们需要额外的计算来恢复精度。

如上图所示,我们可以找到一组缩放值和零点,使得量化之后的整数值 x 经过反量化之后和原始值比较接近。尽管我们需要存储额外的变量(缩放值和零点),由于大量的参数变成了存储更少的整型,模型的占用空间也大大下降了。
在推理的时候,量化的模型需要经过反量化的计算,由于反量化的操作开销比较低、并且大多数情况下模型处于带宽瓶颈,并不会对速度有明显影响;尽管量化还原之后有误差,但是不同的量化策略可以还原模型性能,在实际任务中引起的性能下降可以被接受。
比如 llama.cpp 中使用了 k-quants8,下面图中方块(⬜)表示 FP16 的模型,原点(⚪)表示各种量化模型,横轴是模型权重占用空间大小,纵轴是模型的困惑度(越低越好)。我们可以观察到:

我们再看看表格化的数据。对于一个 Llama 7B 的模型,F16 需要 13.0 GB 的空间,而 Q4_K_S 量化只需要 3.56 GB,并且性能并没有明显下降!这里我们先忽略 k-quants 各种奇怪的后缀命名,如果简单计算 13 GB × 4 / 16 = 3.25 GB 大致上能吻合。

但是奇怪的是在 M2 Max 上,经过量化的模型的出词延迟 ms/tok 居然还变小了!我们前面提到量化模型推理时有额外的反量化计算,这里的原因就是在 M2 Max 上模型速度的瓶颈并不是算力,而是内存带宽,处理器算完了,下一波数据还没搬过来,然而在量化完之后同样的带宽能搬运更多权重。
目前对于大多数模型,如果想保有近似原始模型性能,Q4 几乎是一个极限选择。我们可以通过一个懒人公式,参数量 (B)/2=内存(GB)来估算。但是实际上推理还需要存 KV Cache、激活等缓存变量,尤其是处理长文档的时候。比如对于 Llama2-7B9,每个 token 需要的 KV Cache 大约为 0.00052 GB(半精度),也就是输入输出在 2000 的时候就需要额外 1 GB 内存。
在讨论这个问题之前,我们先定义这里的速度究竟是什么?前面我们提到推理的时候分为两个阶段,对应可以有两类指标评价:
在 PC 游戏和装机领域我们经常会听到 CPU 瓶颈、显卡瓶颈对游戏性能的影响。同样对于 LLM 推理,在大多数情况下对于 GPU 推理来说有这样的结论:在预填充阶段,主要是算力瓶颈,Transformer 并行处理 prompt token 需要大量的计算;在自回归解码阶段,主要是带宽瓶颈,计算不够密集导致参数还没搬过来只能空等。
我们记住几个公式:
对于更加严肃准确一点的推理入门知识,有一个非常好的文章(A guide to LLM inference and performance | Baseten Blog),英文比较好、有一定算法基础的同学可以跳转观看。我们这里引用其中的部分计算作简单介绍。
对于硬件来说我们可以定义一个 ops/byte 的性能指标,即搬运多少数据时可以做多少计算。注意在 LLM 推理中,无论模型是否量化,计算都应该在 FP16 或者更高的精度。所以我们在衡量算力的时候应该用 FP16 的指标,比如 4060Ti 的 FP16 算力是 22.06 TFLOPS(每秒执行浮点数计算的次数)。这个算力除以内存或者显存带宽就可以算出相应指标,比如 4060Ti 的显存带宽是 288 GB/s,即 76.6 ops/byte。但是对于专用的推理卡比如 A10,FP16 算力为 125 TFLOPS,24 GB GDDR6 的显存带宽达到 600 GB/s,则能达到 208 ops/byte,
同时我们可以计算网络每部分同样单位的计算密度(arithmetic intensity),比如当上下文长度为 4096 时,Llama 2 注意力部分的计算密度为 62 ops/byte。这个值都比上面两个值低,因此如果我们的模型服务每次只处理一次请求,那么解码时基本上是处于带宽瓶颈。如果要充分利用卡的算力,就需要使用 batching 等方法。
很多读者看到上面的介绍应该已经一头包了。厂商也利用了这里面的信息差,开始宣传各种新产品的 AI 算力。这给我们的设备选购带来了非常大的干扰。
先说 CPU 部分,几乎每家都在宣传其 NPU 算力,但是实际上这个算力是非常难利用的,基本上 NPU 是为了低功耗的整型计算设计的,比如 AMD Ryzen™ AI 7 PRO 360 在产品页10标注的 AI 算力高达 72 TOPS。注意这个单位和 TFLOPS 的区别,这里这是整数运算,实际上几乎没有大模型能以纯整型进行推理。

尽管类似 Apple Neural Engine 是可以支持半精度运算的,但除了苹果自己之外并没有第三方推理引擎支持11。
再说 GPU 部分,对于消费级的产品,厂商也是费劲心思让消费者没有办法辨别卡的真实算力,比如下面的 4060Ti 的参数表里面又出来了一个 353 AI TOPS,由于是在没找到哪里有标注实际计算方式,推测是整型加稀疏化的结果。

鉴于相当模糊的现状、有上手难度的计算方式,最简单的办法就是看社区汇报的类似硬件的推理速度。llama.cpp 非常贴心地总结了 Apple Silicon 对于 Llama 7B 的推理速度12(输入 512, 输出 128):
| 带宽 GB/s | GPU 核心数 | F16 预填充 [t/s] | F16 解码 [t/s] | Q8 预填充 [t/s] | Q8 解码 [t/s] | Q4 预填充 [t/s] | Q4 解码 [t/s] | |
|---|---|---|---|---|---|---|---|---|
| M2 | 100 | 10 | 201.34 | 6.72 | 181.4 | 12.21 | 179.57 | 21.91 |
| M2 Ultra | 800 | 60 | 1128.59 | 39.86 | 1003.16 | 62.14 | 1013.81 | 88.64 |
| M4 | 120 | 10 | 230.18 | 7.43 | 223.64 | 13.54 | 221.29 | 24.11 |
| M4 Max | 546 | 40 | 922.83 | 31.64 | 891.94 | 54.05 | 885.68 | 83.06 |
这里我们也能比较直观地看到:
我们再来验证一下前面提到的公式:
对于 N 卡来说速度会快很多,比如根据其博客15,Llama 3 8B Q4 的模型,4060Ti 就可以跑到接近 55 tokens/s(当然这里输入 100,输出 100,比上面那张表要小)。同样可以通过上面的公式简单验算。

我们再看看纯 CPU 推理工况下的表现,比如下图16,AMD 和 Intel 两款移动端「AI」处理器的表现,实际上比 M4 还要慢一半。这主要还是由于搭载内存的带宽限制。

这里我们再次强调:
个人认为对于大多数人设备选购还是遵循「进可 LLM,退可娱乐」的原则。不同人有不同的需求,并且预算、功耗、场景都有所不同。如果说最优性价比的选择,我觉得是:
关联阅读:
> 关注 少数派小红书,感受精彩数字生活 🍃
> 实用、好用的 正版软件,少数派为你呈现 🚀
© 本文著作权归作者所有,并授权少数派独家使用,未经少数派许可,不得转载使用。