这两天看了下 Deepseek v3.1 的信息,关注到了
UE8M0
,刚好也是蹭一下寒武大帝
的热度,今天这篇文章来和大家聊一聊关于 LLM 里面经常被提起的 精度 问题。
在深入探讨驱动现代人大语言模型(LLM)的复杂计算之前,我们必须首先回归本源,理解计算机处理信息的最基本方式。数字世界的宏伟建筑,都始于一个最微小、最不起眼的砖块:比特(bit)。
在计算机内部,信息以 bit
的形式存在,bit
是存储的原子单位,是不可再分的最小单元。它只能存储两个值之一:0 或 1。我们常说的**“计算机里的一切都是 0 和 1”** ,指的就是比特 。从物理层面看,任何具有两种稳定状态的系统都可以存储一个比特。在芯片中,这表现为电荷的有无或电压的高低;在硬盘上,则表现为磁介质的南北极性。
但是我们也知道,单个比特所能承载的信息量微乎其微,就像一个只能回答“是”或“否”的开关,表示能力极为有限。为了表达更复杂的概念,计算机需要将这些原子单位组合起来,形成一种更具表达能力的语言。
为了让 bit
变得更加实用,计算机界的前辈们将它们进行了分组。现代计算领域公认的标准是将 8 个 bit
组合在一起,形成一个“字节”(Byte)。这个由 8 个 0 和 1 组成的序列(例如 01011010
)是当今绝大多数计算机体系结构中最小的可寻址内存单元。这意味着,尽管处理器可能一次性处理 32 位或 64 位的数据,但它能独立定位和访问的最小内存块就是一个字节。
不过 8
bit
构成一字节并不是什么物理定律,也是在工程上探索出的标准。历史上,也曾出现过使用 6bit
或 9bit
作为 1 个字节的计算机系统。
bit
组合的表示能力在于其指数级的增长潜力。n
个比特可以产生 2n 种不同的模式。
因此,一个由 8 个比特组成的字节,可以表示 28 = 256 种模式,计算机的魔力就在于为这 256 种模式赋予特定的含义。
这些二进制模式如何转化为我们能理解的信息?答案是 编码方案。
然而,这种表示方式存在一个固有的局限。当一个计算结果超出了其数据类型所能表示的最大值时,就会发生 “整数溢出”(Integer Overflow)。例如,对一个 32 位整数的最大值 2,147,483,647 再加 1,结果不会是 2,147,483,648,而是会“回卷”到一个巨大的负数 -2,147,483,648。
@Test
public void test_integer() {
Integer a = 2147483647;
Integer b = a + 1;
// b = -2147483648
System.out.printf("b = %d%n", b);
}
这并非一个简单的程序错误,而是用有限数量的比特来表示无限集合的数字时不可避免的根本性限制。
整数溢出问题恰恰是我们探讨数值精度问题的第一个,也是最简单的例证,它揭示了贯穿本文的核心主题:计算机中的所有数值表示都是对真实数学世界的近似或子集。理解这些表示方式的局限性、权衡和妥协,是理解更高级计算(尤其是 AI 计算)中精度挑战的关键。整数溢出,便是这场与有限性抗争的序幕。
整数的世界虽然整洁有序,但对于描绘我们复杂多变的物理世界而言,却有点得力不从心。科学研究、工程设计、金融分析等领域,无一不涉及小数、分数以及那些无法用有限小数表示的无理数(如圆周率 π)。更重要的是,这些领域常常需要同时处理尺度差异极大的数值,比如计算星系间的距离(以光年计)和测量原子内质子间的距离(以飞米计)。用整数来处理这些问题,无异于缘木求鱼。
整数表示法的核心缺陷在于其“固定”性。小数点的位置是固定的(隐含在最右边),这使得它无法表示小数部分。此外,其数值是均匀分布的,数字 5 和 6 之间的间隔,与 5,000,000 和 5,000,001 之间的间隔完全相同,都是1。这种均匀性在需要表示极大或极小数值时,会造成巨大的空间浪费和效率低下。
为了解决这个问题,计算机科学家们从一个早已存在的数学工具中找到了新的思路:科学记数法 。一个非常大或非常小的数,例如阿伏伽德罗常数,可以表示为 6.022 * 10^23
这种表示法巧妙地将一个数拆分为两部分:
6.022
,它决定了数值的“精度”。23
,它决定了数值的“量级”或小数点的位置。这种“浮动”小数点的方法,使得我们能用相对紧凑的形式,表示出范围极其广阔的数值。计算机中的浮点数(Floating-Point Number)正是这一思想的二进制实现。
在早期计算时代,不同计算机制造商对浮点数的实现方式各不相同,导致同一个程序在不同机器上运行可能会得出不同的结果。为了终结这种混乱局面,电气和电子工程师协会(IEEE)在 1985 年推出了 IEEE 754 标准,为浮点数算术定义了一套通用、严谨的规范。如今,从智能手机到超级计算机,几乎所有硬件的浮点数单元都遵循这一标准,确保了计算结果的一致性和可移植性。
根据 IEEE 754 标准,一个浮点数同样由固定的比特序列表示,但这些比特被划分为三个部分,共同定义一个数值。我们以最常见的 32 位单精度浮点数(FP32)为例来解析其结构:
尽管浮点数系统设计精妙,但它依然受限于有限的比特数。尾数位长度是有限的,这意味着绝大多数实数都无法被精确表示。这个过程必然伴随着 “舍入误差”(Rounding Error),这是浮点数计算与生俱来的特征。
一个经典的例子是十进制数 0.1,它在十进制中是有限的,但在二进制中却是一个无限循环小数 (0.0001100110011...
) 。当计算机试图用有限的尾数位来存储 0.1 时,它只能截取或舍入这个无限序列,从而得到一个非常接近但不完全等于 0.1 的近似值。这种微小的误差在单次计算中可能无伤大雅,但在数百万次乃至数十亿次的迭代计算(例如训练一个大型神经网络)中,它们会不断累积,可能导致最终结果出现显著偏差。
浮点数的核心设计哲学,是以牺牲均匀分布为代价,换取巨大的动态范围。这并非一个缺陷,而是其设计的精髓所在。整数在数轴上是等距的,而浮点数在 0 附近非常密集,可以表示极小的数值;随着数值变大,相邻可表示数之间的间隔也急剧增大 。这种非均匀分布的设计,使得固定的比特数能够覆盖从原子到宇宙的广阔尺度。它体现了一个根本性的权衡问题:在科学计算中,相对误差通常比绝对误差更重要。浮点数系统致力于在不同量级上都保持一定数量的有效数字,而这正是其强大功能与内在局限性的根源。这一核心权衡,也将在我后续探讨更低精度格式时出现。
在浮点数的世界里,不同的比特分配方案催生了不同的精度格式。其中,64 位双精度(FP64)和 32 位单精度(FP32)长期以来扮演着截然不同的角色,一个作为科学计算的黄金标准,另一个则成为 AI 和图形领域无可争议的主力军。
FP64,顾名思义,使用 64 个比特来表示一个数字。其内部结构如下:
这种“奢侈”的比特分配带来了两个显著优势:巨大的动态范围和极高的精度。11 位的指数使其能够表示的数值范围远超 FP32,而 52 位的尾数则提供了大约 15 到 17 位的十进制有效数字精度 。
在传统的科学计算和工程模拟领域,FP64 是不可或缺的“黄金标准”8。在这些应用中,例如气候模型、流体力学模拟或高能物理计算,微小的舍入误差在长时间的迭代中可能会被放大,导致结果失之毫厘,谬以千里。FP64 的高精度能够有效地抑制这种累积误差,确保计算结果的可靠性和准确性。
与 FP64 相比,FP32 是一种更为折衷的方案。它使用 32 个比特,结构如下 8:
FP32 提供了大约 7 位的十进制有效数字精度,虽然远不及 FP64,但对于许多应用来说已经“足够好”。它在动态范围和精度之间取得了良好的平衡,更重要的是,它在计算效率和内存占用上具有巨大优势。处理一个 FP64 数字所需的计算资源和内存带宽远高于 FP32。
FP32 的崛起与图形处理器(GPU)的发展密不可分。GPU 的设计初衷是为了满足电子游戏产业对实时图形渲染的巨大需求。在渲染三维场景时,计算像素的位置、颜色和纹理等信息,FP32 提供的精度和性能恰好达到了一个“甜点”。因此,像 NVIDIA 这样的公司花费了数十年时间,将它们的硬件架构针对 FP32 算术进行了深度优化。
当深度学习革命爆发时,研究人员发现神经网络中的核心运算:即大规模矩阵乘法,本质上是高度并行的,这与 GPU 的图形渲染任务在计算模式上不谋而合。于是,现成的、为游戏优化的 GPU 成为了训练神经网络的理想硬件。
在这一历史背景下,FP32 自然而然地成为了 AI 领域的默认标准。神经网络的训练过程具有统计特性,对噪声有一定的容忍度,FP32 提供的精度被证明足以稳定地训练绝大多数模型。它既避免了 FP64 带来的沉重计算和内存开销,又比更低精度格式(在当时还不普遍)提供了更好的数值稳定性。
因此,AI 领域对 FP32 的最初拥抱,并非源于对神经网络数值需求的理论推导,而更多地是一次历史的、经济的“偶然”。AI 研究者们利用了游戏产业催生出的强大且高度优化的 FP32 计算工具。这种路径依赖深刻地影响了 AI 硬件的早期发展轨迹。直到近年来,随着 AI 专用加速器的兴起,业界才开始真正摆脱这种历史惯性,转而根据神经网络独特的统计特性,去设计全新的、更高效的数值格式,例如我们稍后将要讨论的 BFloat16 和 FP8。
随着大语言模型的参数量从数亿、数十亿爆炸式增长到数千亿甚至万亿,FP32 曾经的“主力军”地位开始动摇。训练和部署这些庞然大物所带来的巨大压力,迫使整个行业进行一场深刻的“数值压缩”革命,目标直指 16 位精度。
驱动这场变革的核心动力是“规模化危机”(The Scaling Crisis),使用 FP32 训练一个拥有数千亿参数的模型,在几个关键方面都变得难以为继 :
175 * 10^9 * 4
bytes,即 700 GB 的显存,远超任何单张 GPU 的容量。将精度从 32 位降至 16 位,可以直接将模型所需的内存减半,并在支持低精度计算的专用硬件(如 NVIDIA 的 Tensor Cores)上将计算吞吐量提升一倍以上。这巨大的效率收益,让 16 位精度成为不可阻挡的趋势。然而,如何实现这“伟大的压缩”却引发了一场技术路线之争,催生了两种截然不同的 16 位标准。
FP16 是 IEEE 754 标准中定义的半精度格式,其结构如下:
与 FP32 相比,FP16 的设计选择是大幅削减指数位(从 8 位降至 5 位),同时保留一个相对可观的尾数位(从 23 位降至 10 位)。这种设计的哲学是,在大幅缩小的数值范围内,尽可能地保持较高的相对精度。
BFloat16(Brain Floating Point)是由 Google 专为机器学习应用设计的格式,它做出了完全不同的权衡:
BFloat16 的设计极为巧妙:它保留了与 FP32 完全相同的 8 位指数位,这意味着它拥有和 FP32 一样广阔的动态范围,代价是尾数位被大幅削减至仅 7 位,导致其精度甚至低于 FP16。为了更直观地理解这些差异,下表对主流浮点数格式进行了总结:
表 1:主流浮点数格式对比
格式 | 总位数 | 符号位 | 指数位 | 尾数位 | 近似十进制精度 | 核心设计 |
---|---|---|---|---|---|---|
FP64 | 64 | 1 | 11 | 52 | ~15-17 位 | 极高精度,极大范围 |
FP32 | 32 | 1 | 8 | 23 | ~7 位 | 平衡的精度与范围 |
FP16 | 16 | 1 | 5 | 10 | ~3-4 位 | 牺牲范围,保留精度 |
BFloat16 | 16 | 1 | 8 | 7 | ~2-3 位 | 牺牲精度,保留范围 |
从表中可以清晰地看到 FP16 和 BFloat16 之间根本性的设计分歧。这场分歧的最终走向,揭示了深度学习训练过程中的一个核心数值特性:对于训练的稳定性而言,动态范围比精度更为重要。
FP16 虽然精度更高,但其 5 位的指数位所能表示的动态范围非常狭窄。在神经网络的反向传播过程中,梯度值(尤其是深层网络中)的量级可能非常小,很容易低于 FP16 所能表示的最小正数,从而发生“下溢”(Underflow),直接变成 0。梯度一旦为 0,参数更新就会停止,训练过程便会停滞甚至崩溃。
BFloat16 的设计正是为了解决这一痛点。通过保持与 FP32 相同的 8 位指数,它确保了任何在 FP32 中不会溢出的数值,在 BFloat16 中同样不会溢出(尽管会损失大量精度)。这使得 BFloat16 成为一个更“即插即用”的 FP32 替代品,极大地简化了从 FP32 到 16 位训练的迁移过程,因为模型对梯度消失的敏感度远高于对梯度值被轻微舍入的敏感度。
尽管 BFloat16 在范围上更具优势,但无论是 FP16 还是 BFloat16,直接用于完整的训练流程都存在风险。为了兼顾 16 位计算的速度和 32 位计算的稳定性,大佬们又提出和开发出了一种强大的技术:混合精度训练(Mixed-Precision Training) ;
其核心思想如下:
对于动态范围尤其受限的 FP16,混合精度训练还需要一个额外的关键技术:损失缩放(Loss Scaling) ;其原理是:
S
(例如 256)。S
倍。S
,恢复其原始大小。为了应对训练过程中梯度范围的动态变化,通常采用 动态损失缩放(Dynamic Loss Scaling),即训练框架会自动调整 S
的大小:如果在一定迭代次数内没有出现溢出(梯度值过大),就增大 S
;如果检测到溢出,就减小 S
并跳过此次权重更新。
关于 动态损失缩放(Dynamic Loss Scaling) 可以参考:https://www.graphcore.ai/posts/training-large-models-more-stably-with-automatic-loss-scaling
混合精度训练和损失缩放技术的成熟,最终为大模型全面进入 16 位时代铺平了道路,成为在算力、内存和模型性能之间达成精妙平衡的关键所在。
在 16 位精度已成为主流的背景下,对计算效率的极致追求并未停歇。随着硬件的不断革新,AI 社区将目光投向了更低的精度——8 位。从 16 位到 8 位,每一次比特数的减半,都意味着理论上内存占用再次减半,以及在兼容硬件(如 NVIDIA Hopper 和 Blackwell 架构的 GPU)上计算吞吐量的翻倍。这对于训练和推理成本呈指数级增长的 LLM 而言,是难以抗拒的诱惑。
在进入 8 位浮点数(FP8)的世界之前,有必要先了解 8 位整数(INT8)量化。
与 16 位精度之争相似,8 位浮点数的标准化也面临着如何在极度有限的 8 个比特内分配指数和尾数的权衡问题。业界(由 NVIDIA、Intel 和 ARM 等巨头联合提出)主流的 FP8 格式包含两种变体,它们被设计用于神经网络训练和推理过程中的不同部分:
E5M2 (1-5-2) | E4M3 (1-4-3) | |
---|---|---|
结构 | 1 位符号,5 位指数,2 位尾数 | 1 位符号,4 位指数,3 位尾数 |
设计哲学 | 分配了更多的比特给指数,优先保证了动态范围。5 位的指数使其能够表示的数值范围远大于 E4M3 | 一种更为均衡的格式,牺牲了一位指数,换来了一位额外的尾数,从而获得了更高的精度 |
场景 | 适合处理数值波动剧烈的张量,例如梯度。在反向传播过程中,梯度值的大小可能变化数个数量级,E5M2 的宽范围能有效防止上溢或下溢,确保训练的稳定性 | 对于模型的权重和激活值,其数值分布通常比梯度更稳定。在这些场景下,稍高的精度(E4M3)对于维持模型性能更为有利,而其动态范围也足以覆盖绝大多数情况 |
两种主流 FP8 格式的出现,本身就标志着 AI 数值优化的新阶段,系统不再是简单地为整个模型选择一个“一刀切”的精度标准。相反,它开始根据神经网络中不同张量(权重、激活、梯度)的独特数学特性,在单次训练迭代中动态地应用不同的、高度专业化的数值格式。例如,在前向传播中使用 E4M3 计算激活值,在反向传播中使用 E5M2 计算梯度。这种细粒度的、区别对待的优化策略,是硬件-软件协同设计走向成熟的重要体现,它将计算效率的压榨推向了新的高度。
正当业界围绕 E4M3 和 E5M2 构建 FP8 生态时,DeepSeek 在 V3.1 模型的发布中,抛出了一个全新的概念:“UE8M0 FP8 scale data format”。这个看似晦涩的技术术语,不仅揭示了低精度技术演进的新方向,更折射出全球 AI 产业链背后深刻的战略博弈(这里手动先祝贺下国产芯片的股民们)。
UE8M0 最直接的解读是 Unsigned Exponent 8, Mantissa 0(8 位无符号指数,0 位尾数),这意味着在分配给数值本身的 8 个比特中(可能不含符号位,由其他机制处理),全部用于指数部分,而尾数部分为零。
一个 M0 格式的数值,如果孤立存在,几乎毫无用处。它的价值完全体现在作为“尺度数据格式”(scale data format) 与 微缩放(Microscaling)或 块浮点(Block Floating-Point)的技术结合使用时。其工作原理大致如下:
在这种机制下,“精度”并没有消失,而是被 转移了,它不再由每个 8 位数值的尾数位承载,而是被集中存储在那个高精度的共享缩放因子中。而 8 位的 UE8M0 数据本身,则变成了一个极其简单的、只表示相对量级的索引,硬件处理起来异常高效(乘法可以简化为更快的位移操作)。
这种设计是“范围优先”理念的逻辑终点。从 FP16 到 BFloat16,再到 E4M3 到 E5M2,我们已经看到了动态范围在 AI 训练中压倒精度的趋势。UE8M0 将此推向极致:将所有比特都交给指数,以应对最极端的数据分布,而将精度问题完全外包给更高层级的元数据——缩放因子。这标志着一种架构上的转变,未来的低比特格式可能不再是独立的数值类型,而是由简单核心数据类型和复杂结构化元数据(如块缩放因子)共同组成的复合系统。
比技术本身更重要的是 DeepSeek 官方传递的信息:**UE8M0 是“为即将发布的下一代国产芯片设计的” **。NVIDIA 的行业霸主地位不仅在于其强大的 GPU 硬件,更在于其 CUDA 软件生态以及软硬件的深度绑定,包括其 FP8 格式(E4M3/E5M2)与 Tensor Core 单元的紧密集成优化。任何新的硬件竞争者都难以简单复制这一套复杂的体系。
UE8M0 的推出,可以看作是由软件和算法领导者(DeepSeek)牵头,为即将到来的国产 AI 芯片(如壁仞科技、燧原科技等)定义一个统一、优化的数值计算标准。这是一种“软件定义硬件”的策略:
因此,UE8M0 的诞生,既是技术演进的产物,更是地缘政治和产业链重构背景下的战略选择。它是一个信号,标志着中国 AI 产业正试图从最底层的数字语言开始,构建一套独立、平行的发展路径;
表 2:8 位数值格式版图
格式 | 总位数 | 指数位 | 尾数位 | 核心哲学 | 主要应用场景 |
---|---|---|---|---|---|
INT8 | 8 | 0 | 7 (含符号) | 均匀间隔,固定范围 | 推理加速,对异常值敏感 |
E4M3 | 8 | 4 | 3 | 平衡的精度与范围 | 权重和激活值 |
E5M2 | 8 | 5 | 2 | 范围优先 | 梯度 |
UE8M0 | 8 | 8 (无符号) | 0 | 极致范围,精度外置 | 与微缩放结合,用于国产芯片 |
对计算效率的探索永无止境,在 8 位精度逐渐成为现实的同时,研究和工程界已经将目光投向了更具挑战性的领域:4 位浮点数(FP4)乃至更低的比特格式。这不仅是对技术极限的冲击,也预示着 AI 计算的未来形态,NVIDIA 最新的 Blackwell 架构已经为 FP4 推理提供了硬件支持从 8 位到 4 位,带来的潜在收益是巨大的:内存占用和数据传输量再次减半,理论计算吞吐量在专用硬件上可以再翻一倍。对于需要部署在资源受限的边缘设备(如手机、汽车)或需要极致推理速度的云端应用,FP4 具有无与伦比的吸引力。
从计算机诞生之初的比特,到科学计算的黄金标准 FP64,再到由游戏产业推向主流的 FP32,直至 AI 时代为追求极致效率而催生的 FP16、BFloat16、FP8 乃至充满战略色彩的 UE8M0,数值精度的演进史就是一部关于权衡与妥协的历史。
这场演进背后,始终是三股力量在相互博弈、协同进化:
理解这三者之间的相互作用——从一个比特的 0 与 1,到一个可能影响全球 AI 格局的 UE8M0 格式——就是理解人工智能未来发展轨迹的关键。
这场在比特与 FLOPS 之间的伟大平衡之舞,远未到落幕之时。