Connect with us

人工智能

微软的推理框架将 1 位大型语言模型带到本地设备

mm
Understanding 1-bit LLMs and Microsoft's BitNet.cpp Framework

2024 年 10 月 17 日,微软宣布了 BitNet.cpp,这是一种旨在运行 1 位量化大型语言模型(LLM)的推理框架。BitNet.cpp 是通用 AI 的一个重大进步,它可以在标准 CPU 上高效部署 1 位 LLM,无需昂贵的 GPU。这一发展使 LLM 的访问变得民主化,使其可在广泛的设备上使用,并为本地 AI 应用程序带来新的可能性。

了解 1 位大型语言模型

大型语言模型(LLM)传统上需要大量的计算资源,因为它们使用高精度浮点数(通常为 FP16 或 BF16)来表示模型权重。这一必要性使得部署 LLM变得昂贵和耗能。

在其核心,1 位 LLM 使用极端量化技术来表示模型权重,仅使用三个可能的值:-1、0 和 1,因此得名“1.58 位”(因为它需要稍多于一位来编码三个状态)。

三元权重系统

概念

BitNet.cpp 中的 1 位量化是一种三元权重系统。BitNet 只使用三个可能的值来表示每个参数:

  • -1(负数)
  • 0(中性)
  • 1(正数)

这导致每个参数的存储需求约为 1.58 位,因此得名 BitNet b1.58。参数位宽的这种大幅减少导致内存使用和计算复杂性大大降低,因为大多数浮点乘法被替换为简单的加法和减法。

数学基础

1 位量化涉及通过以下步骤将权重和激活转换为其三元表示:

1. 权重二值化

二值化权重涉及将其集中在均值(α)周围,从而得到三元表示。转换的数学表示为:

Wf=Sign(Wα)

其中:

  • W 是原始权重矩阵。
  • α 是权重的均值。
  • Sign(x) 如果 x > 0 返回 +1,否则返回 -1

2. 激活量化

量化激活确保输入被限制在指定的位宽内:

其中:

  • Qb = 2(b−1)2^{(b-1)}b 位宽的最大量化级别。
  • γx 的最大绝对值(表示为 ∣∣x∣∣∞)。
  • ε 是一个小数,用于防止计算过程中的溢出。

3. BitLinear 操作

BitLinear 层用简化的操作替换了传统的矩阵乘法:

y=Wf×x^e×(Qbβγ)

其中:

  • β 是一个用于最小化近似误差的缩放因子。
  • γ 缩放激活。
  • Q_b 是量化因子。

这种转换使得计算变得高效,同时保持了模型的性能。

性能影响

内存效率

三元权重系统大大降低了内存需求:

  • 传统 LLM:每个权重 16 位
  • BitNet.cpp:每个权重 1.58 位

这种降低转化为大约 90% 的内存节省,相比传统的 16 位模型,这使得更大的模型可以在相同的硬件约束下运行。

能效

推理速度,能效(Apple M2)

 

推理速度:在两个 CPU 上都更快

推理速度,能效(i7-13700H)

1. 推理速度:在两个 CPU 上都更快

推理速度 表示为每秒处理的令牌数量。以下是观察结果的总结:

  • 在 Apple M2 Ultra 上: BitNet.cpp 实现了最高 5.07 倍 的速度提升,用于更大的模型(30B),相比 Llama.cpp,其峰值速度为 593.43 个令牌每秒,对于 125M 模型,这是一个 1.37 倍 的速度提升。对于更大的模型,如 3.8B 和 7B,BitNet.cpp 在各个规模上保持了超过 84.77 个令牌每秒的速度,展示了其效率。
  • 在 Intel i7-13700H 上: BitNet.cpp 实现了更显著的速度提升。在 7B 模型大小上,BitNet.cpp 相比 Llama.cpp 提供了 惊人的 5.68 倍 速度提升。对于较小的模型,如 125M,它处理了 389.08 个令牌每秒,这比 Llama.cpp 快 2.37 倍

2. 能效:边缘设备的游戏规则改变者

提供的图表还包括 能耗比较,显示出每处理一个令牌的能耗大大降低:

  • 在 Apple M2 Ultra 上: BitNet.cpp 的能耗节省显著。对于 700M 模型,它每处理一个令牌的能耗比 Llama.cpp 少 55.4%,从 0.314 降低到 0.140。这种趋势在更大的模型中继续,70B 模型显示出 70.0% 的能耗降低。
  • 在 Intel i7-13700H 上: BitNet.cpp 提供了 71.9% 的能耗节省,用于 700M 模型,能耗从 1.367 降低到 0.384。尽管 Llama.cpp 中 70B 模型的能耗数据不可用,但 BitNet.cpp 仍然保持高效,其能耗为 17.33,用于 70B 模型。

3. 超越人类阅读速度基准

这些图表中最有趣的见解之一是对 人类阅读速度 的引用,标记为 5-7 个令牌每秒。这条红线表明,两种实现,尤其是 BitNet.cpp,都可以轻松超越人类阅读速度,甚至对于最大的模型:

  • Apple M2 Ultra 上,BitNet.cpp 的速度在所有模型大小上都超越了人类阅读速度,70B 模型的最低速度为 8.67 个令牌每秒
  • Intel i7-13700H 上,100B 模型的速度为 1.70 个令牌每秒,几乎达到人类阅读速度的下限,而所有较小的模型都超越了这一基准。

训练考虑

直通估计器(STE)

由于 1 位量化引入了非可微函数,训练涉及一种称为 直通估计器(STE) 的专用技术。在这种方法中,梯度通过非可微点保持不变。以下是 Python 中的简化实现:

类 StraightThroughEstimator(Function):
@staticmethod
def forward(ctx, input):
return input.sign()

@staticmethod
def backward(ctx, grad_output):
return grad_output

混合精度训练

为了在训练过程中保持稳定性,使用 混合精度

  • 权重和激活:量化为 1 位精度。
  • 梯度和优化器状态:以更高的精度存储。
  • 潜在权重:以高精度维护,以便在训练过程中进行准确的更新。

大学习率策略

1 位模型的一个独特挑战是,小更新可能不会影响二值化的权重。为了减轻这一问题,学习率被增加,以确保更快的收敛和更好的优化,相比传统方法。

组量化和归一化

BitNet.cpp 引入了 组量化和归一化,以增强模型并行性。与其计算整个权重矩阵的参数,BitNet 将权重和激活分为多个组(G)。

这种分组允许在不需要额外的组间通信的情况下进行高效的并行处理,从而实现大规模模型的训练和推理。

实现说明和优化

CPU 优化

BitNet.cpp 利用了多个低级优化来实现峰值 CPU 性能:

  • 矢量化操作:利用 SIMD 指令高效地执行位操作。
  • 缓存友好内存访问:结构化数据以最小化缓存缺失。
  • 并行处理:有效地将工作负载分配到多个 CPU 核心。

以下是 BitNet 中一个关键函数的示例,实现了量化和推理:

def bitlinear_forward(input, weight, scale):
# 使用 absmax 量化输入
input_q = quantize(input)

# 执行二进制矩阵乘法
output = binary_matmul(input_q, weight)

# 将输出缩放以匹配原始精度
return output * scale

def quantize(x):
# 执行 absmax 量化
scale = torch.max(torch.abs(x))
return torch.clamp(x / scale, -1, 1) * scale

支持的模型

BitNet.cpp 的当前版本支持以下在 Hugging Face 上的 1 位 LLM

  • bitnet_b1_58-large(0.7B 参数)
  • bitnet_b1_58-3B(3.3B 参数)
  • Llama3-8B-1.58-100B-tokens(8.0B 参数)

这些模型公开提供,以展示框架的推理能力。虽然它们并非由微软官方训练或发布,但它们展示了框架的多功能性。

安装指南

要开始使用 BitNet.cpp,请按照以下步骤:

先决条件

  1. Python >= 3.9
  2. CMake >= 3.22
  3. Clang >= 18
  4. Conda(强烈推荐)

对于 Windows 用户,应安装 Visual Studio,并启用以下组件:

  • 使用 C++ 的桌面开发
  • 适用于 Windows 的 C++-CMake 工具
  • 适用于 Windows 的 Git
  • 适用于 Windows 的 C++-Clang 编译器
  • MS-Build 支持 LLVM 工具集(Clang)

对于 Debian/Ubuntu 用户,提供了自动安装脚本:

bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"

逐步安装

  1. 克隆存储库
    git clone --recursive https://github.com/microsoft/BitNet.git

    cd BitNet
  2. 安装依赖项
    # 创建一个新的 Conda 环境(推荐)
    conda create -n bitnet-cpp python=3.9
    conda activate bitnet-cpp


    pip install -r requirements.txt
  3. 构建和准备项目:您可以直接从 Hugging Face 下载一个模型并将其转换为量化格式:
    python setup_env.py --hf-repo HF1BitLLM/Llama3-8B-1.58-100B-tokens -q i2_s

    或者手动下载和转换模型:

    huggingface-cli download HF1BitLLM/Llama3-8B-1.58-100B-tokens --local-dir models/Llama3-8B-1.58-100B-tokens

    python setup_env.py -md models/Llama3-8B-1.58-100B-tokens -q i2_s

使用 BitNet.cpp 运行推理

要使用框架运行推理,请使用以下命令:

python run_inference.py -m models/Llama3-8B-1.58-100B-tokens/ggml-model-i2_s.gguf -p "Sandra 前往厨房。Sandra 在哪里?" -n 6 -temp 0.7

解释:

  • -m 指定模型文件路径。
  • -p 定义提示文本。
  • -n 设置要预测的令牌数量。
  • -temp 调整推理过程中的采样随机性(温度)。

输出示例

Sandra 前往厨房。哪里是 Sandra?

答案:Sandra 在厨房。

BitNet.cpp 的技术细节

BitLinear 层

BitNet.cpp 实现了一个修改的 Transformer 架构,用 BitLinear 操作替换了标准的矩阵乘法。这种方法将权重集中在零附近,然后缩放以减少近似误差。关键的转换函数如下:


# 二值化权重的函数,用于 1 位权重
def binarize_weights(W):
alpha = W.mean()
W_binarized = np.sign(W - alpha)
return W_binarized

集中权重和缩放的组合确保量化误差保持最小,从而保持性能。

行业影响

BitNet.cpp 可能对 LLM 的部署产生深远的影响:

  • 可访问性:允许 LLM 在标准设备上运行,民主化了对强大 AI 的访问。
  • 成本效率:降低了对昂贵 GPU 的需求,降低了采用门槛。
  • 能效:通过利用标准的 CPU 推理节省能量。
  • 创新:为不依赖云的本地 AI 应用程序(如实时语言翻译、语音助手和注重隐私的应用程序)开辟了新的可能性。

挑战和未来方向

虽然 1 位 LLM 具有前景,但仍存在几个挑战。这些包括开发用于多样化任务的强大 1 位模型、优化用于 1 位计算的硬件以及鼓励开发人员采用这种新范式。此外,探索 1 位量化用于计算机视觉或音频任务代表了一个令人兴奋的未来方向。

结论

微软的 BitNet.cpp 发布是一个重大进展。通过在标准 CPU 上高效地实现 1 位推理,BitNet.cpp 为 AI 的可及性和可持续性创造了条件。该框架为更便携、更具成本效益的 LLM铺平了道路,推动了本地 AI 的可能性。

我过去五年一直沉浸在令人着迷的机器学习和深度学习世界中。我的热情和专业知识使我能够为超过50个不同的软件工程项目做出贡献,特别注重人工智能/机器学习。我的持续好奇心也使我对自然语言处理产生了兴趣,这是一个我渴望进一步探索的领域。