← 返回
IT技术

在GPU云上使用SparseGPT和Wanda进行高效的LLM压缩

✍️ zhirenhun 📅 2026/9/1 👁 125 阅读 ⏱ 32 分钟
在GPU云上使用SparseGPT和Wanda进行高效的LLM压缩

训练万亿参数模型成本高昂,而推理才是持续产生的运营开销。每个请求都会占用 GPU 显存、内存带宽、计算周期、批处理槽位和服务容量。模型权重必须常驻 GPU 显存。键值缓存(KV cache)会在生成过程中不断增长。服务引擎还必须以合理的延迟,在大量并发用户之间复用这些资源。

我们来做一个简单的思想实验。

存储模型权重所需的内存,等于参数数量乘以每个参数的精度(以字节计)。举例来说,一个 70 亿参数的 FP16 模型,每个权重需要 2 个字节,仅权重本身就要占用约 7 × 10^9 × 2 ≈ 14 GB。这还不包括激活值缓冲区、运行时开销、批处理以及 KV 缓存的占用。可以想见,大模型的显存需求很快就会超出常见 GPU 的 VRAM,而 KV 缓存的占用甚至可能轻松超过权重本身。面对这种增长,一味购买更大的 GPU 并非长久之计。因此,LLM 压缩正成为提升推理效率的关键。

这正是 LLM 压缩如此重要的原因。团队不必不断购置更大的 GPU,完全可以转而构建更小、更高效的模型。压缩方法有好几种:量化、蒸馏、低秩近似和剪枝。本文将聚焦剪枝,更具体地说,是 SparseGPT 剪枝和 Wanda 剪枝。这两种方法都属于后训练(post-training)方法,无需代价高昂的重新训练即可压缩大语言模型。

核心要点

  • 剪枝有助于降低 LLM 推理成本,途径是减少非零权重的数量并缓解内存压力。
  • SparseGPT在保持模型质量方面更胜一筹,尤其是在激进剪枝或追求更高稀疏度时。
  • Wanda速度更快、实现也更简单,因为它省去了 Hessian 矩阵估计、重构求解和权重更新这些步骤。
  • 稀疏模型并不会自动变快;要想获得真正的加速,需要稀疏感知的运行时、稀疏内核,以及硬件层面的支持。
  • 剪枝作为更大规模的推理优化策略的一部分时效果最佳, 结合量化、KV 缓存优化、批处理、缓存、推测解码和路由。

为什么LLM压缩很重要

LLM 推理受四个主要生产瓶颈的限制:

  • GPU 显存容量. 模型权重、KV 缓存、运行时张量以及多个并发请求都必须能够放入内存中。离线测试可能显示模型是“可用的”,但在实际流量条件下,由于活跃序列长度和批次大小增加导致的内存压力,可能会导致失败。
  • M内存带宽. 在自回归解码过程中,模型一次生成一个标记。为了生成每个标记,GPU 必须反复加载模型权重和缓存的注意力状态。这通常会导致解码受内存带宽限制而非计算限制。在这种情况下,原始 FLOPs 更高的 GPU 不一定能带来成比例的加速。
  • L延迟. 面向用户的实际应用需要低 首标记延迟、一致的标记间延迟以及可预测的端到端延迟。您可能拥有一个能够生成高质量答案的模型,但如果响应速度慢,用户体验会受到影响。
  • C成本. 当 GPU 未被充分利用、过度配置或受内存限制阻塞时,GPU 云基础设施成本较高。在大规模应用中,即使是微小的效率提升也能降低每百万标记的成本。

剪枝减少了模型中非零权重的总数。稀疏模型存储了许多零。理论上,这可以降低内存和计算需求。如果推理栈能够利用这种稀疏性——使用压缩存储格式和稀疏矩阵乘法内核——模型可以实现更低的显存占用,吞吐量更高,延迟更低。实际的加速效果取决于运行模型所使用的硬件和软件。仅仅将权重置零而不改变内核不会加速推理。

除了 GPU 服务器之外,压缩技术还能带来新的部署方式。经过修剪的模型可以部署到更小的 GPU、边缘设备,甚至多模型服务器上。内存节省使团队能够在单个 GPU 上共置多个模型,使用更便宜的实例,或在相同硬件预算下跑更大的 batch size。压缩与推理的经济性直接相关:每个 token 的内存占用降低,就会使每个 token 的成本降低。

理解稀疏性:非结构化与结构化

神经网络通常是密集的,也就是说它们的大多数权重都是非零的。相比之下,稀疏网络主要由零值权重组成。网络修剪通过永久将权重设为零来提高稀疏度。对于语言模型而言,有两种尤为重要的稀疏类型:非结构化和结构化。

非结构化稀疏性

非结构化修剪 removes individual weights at random throughout the model.This allows the pruning algorithm maximum flexibility in choosing which weights to remove while minimizing impact on model behavior. Because weights can be removed from the matrix at any position, unstructured sparsity can often maintain accuracy fairly well for a given level of sparsity.

然而,GPU 的设计是为了高效处理密集矩阵运算。权重位置随机的稀疏矩阵会导致不规则的内存访问模式、索引开销以及低内核利用率。如果未使用专门的稀疏矩阵乘法内核,密集矩阵乘法仍会处理零值。因此,非结构化稀疏性本身并不会降低延迟。

结构化稀疏性

结构化剪枝会消除规则结构中的权重,例如行、列、块、通道或固定的权重组。一种硬件友好的结构化模式是 2:4 稀疏性,也称为半结构化稀疏性。在 2:4 稀疏性中,连续四个权重块中的两个值为零。这导致 50% 的稀疏率。

支持的 GPU 可以加速这些固定稀疏模式。NVIDIA 的 Ampere GPU 架构增加了对稀疏张量核心的支持,用于细粒度结构化稀疏性,包括 2:4 模式。理论上,它们相比等效的密集操作可以提供最高 2× 的 矩阵乘法吞吐量。但在实际应用中,端到端延迟的改善会根据模型、运行时、内核和工作负载而有所不同。

传统剪枝与现代 LLM 剪枝

传统的 神经网络 剪枝工作流程由一个三步循环组成:训练一个密集模型。移除不太重要的权重。微调 或重新训练稀疏模型以恢复精度。重复直至达到所需的稀疏程度。反复此过程可以得到非常稀疏的网络,但需要大量计算。

对于数十亿参数的 大型语言模型,此剪枝工作流在实际中不易实现。您需要将巨大的模型加载到内存和分布式 GPU 集群中,然后准备适当的恢复数据。每次剪枝步骤后,您还需要运行微调并评估模型质量,然后才能进入下一阶段的压缩。

一些较旧剪枝方法的另一个限制是它们需要昂贵的二阶近似或迭代权重更新。这些方法可能仍能保持精度,但随着参数规模从百万级扩展到十亿级,将其扩展到更大模型时更具挑战性。

现代 LLM 剪枝方法侧重于训练后的一次性剪枝。它们不是进行迭代微调,而是采用预训练模型,并在其上运行一个小规模的校准数据集,以估计哪些权重不太重要。然后通过剪枝使得模型在一组具有代表性的输入上的输出与原始输出“接近”,而无需完整的重新训练。

SparseGPT 和 Wanda 是此类别中的两种显著方法。SparseGPT 使用基于二阶层重建的方法估计重要性;Wanda 采用一种更简单的激活感知权重重要性评分方法。

SparseGPT:基于重构的一次性剪枝

SparseGPT 是一个 一次性剪枝 方法,适用于巨型 GPT 风格模型。它将剪枝视为逐层稀疏回归问题。给定一个具有权重 W 和校准激活 X 的线性层,我们希望找到一个能够最小化重构误差的剪枝矩阵:

裁剪后的层输出应在给定的一组校准数据下尽可能接近原始层的输出。SparseGPT 尝试匹配该层的输出,而不是简单地丢弃最小的权重。它使用 二阶信息,根据校准激活计算得到,以近似剪枝对层输出的影响。在确定哪些权重需要裁剪后,SparseGPT 会更新剩余权重以补偿被移除的权重并最小化重构误差。

SparseGPT 为何效果良好

SparseGPT 通过结合以下几种思想来平衡精度和效率:

SparseGPT:精确性和保留的分子图谱学习算法

  • 二阶敏感性。 与仅根据权重大小进行剪枝不同,使用近似海森信息根据权重移除对输出重构的影响来对权重进行排序。这样可以保留那些不一定是最大的重要权重。
  • 逐层处理。 剪枝后可以对剩余权重进行微调以降低重构误差。此步骤使得模型行为能够在更高稀疏度下得到保持。
  • 权重补偿。 剪枝后可以调整剩余权重以降低重构误差。此步骤有助于在更高稀疏度下保持模型行为。
  • 与模式的兼容性。 SparseGPT 支持非结构化修剪以及半结构化修剪模式,如 2:4。在 2:4 的情况下,算法会考虑每组四个权重并选择两个保留。这使得修剪方法可以应用于具有专门支持结构化稀疏性的硬件。

得益于这些特性,SparseGPT 在激进修剪时能够保持远优于朴素方法的质量。虽然其复杂度更高,但作者表明它能够扩展到具有数十亿到数千亿参数的模型。

Wanda:一个简单的激活感知修剪方法

Wanda(按权重和激活修剪)是一种轻量级修剪方法,作为基于重建的方法(如 SparseGPT)的替代方案提出。与这些方法不同,它不涉及求解逐层重建问题或估计海森矩阵。相反,它使用了一种更简单的激活感知重要性得分:

这张图解释了Wanda Importance Score的计算公式和概念

该得分的直觉是:如果一个权重具有较大的幅值并且连接到激活强的输入维度,则该权重很重要。幅值较小或连接到激活弱的输入维度的权重更可能具有低重要性得分,因而可以被修剪。Wanda 通过在每个输出基础上移除激活缩放后幅值最小的权重来进行排名。作者强调,Wanda 零重新训练或权重更新——修剪后的模型可以直接使用。Wanda 在 LLaMA 和 LLaMA-2 上的实验中显著优于基于幅值的修剪,并且与更复杂的修剪方法具有竞争力。

为什么 Wanda 具有吸引力

Wanda 的简单性带来了若干好处:

  • 实现简便。 该算法仅需要收集激活范数并对权重进行排序。无需海森近似或求解大规模回归问题。
  • 快速剪枝通道。 剪枝过程中不更新权重,因此对大规模模型进行剪枝的速度比 SparseGPT 快几个数量级。Wanda 的作者 声称,在单个 GPU(具体为 H100)上训练的 70B 模型中,Wanda 的速度比 SparseGPT 快 5–10 倍。
  • 具有竞争力的准确率。 尽管 Wanda 非常简单,但在中等稀疏度下它仍能很好地保持质量。在 Llama‑2 模型上,使用 Wanda 进行无结构剪枝所得到的困惑度接近 SparseGPT,并且远优于纯幅度剪枝。
  • 实验基线。 工程团队应该能够快速运行 Wanda,以便在投资更复杂的技术之前测试稀疏模型在其部署栈中的表现。

权衡在于,在极高稀疏度下,Wanda 可能比 SparseGPT 更快地失去准确率。它不支持权重补偿,主要设计用于无结构剪枝,尽管其实现包含 2:4 和 4:8 模式的选项。

比较 SparseGPT 和 Wanda

请考虑以下表格:

特征 SparseGPT Wanda
方法类型 一次性训练后剪枝 一次性训练后剪枝
主要信号 二阶重构误差最小化 权重幅度 × 激活范数
Hessian 近似
剪枝后权重更新 是(可选)
复杂度 更高 更低
运行时开销 更慢 更快
准确率保持 优秀 很好
实现难度 中等 简单
最佳使用场景 高稀疏度且质量保持强 快速基线 p

当你需要保持精度且有工程资源可用时,SparseGPT 表现出色。Wanda 更适合快速实验、轻量级部署,或者近似答案已经足够的情况。许多团队会对两种方法进行基准测试,以找到适合自身模型、稀疏目标和硬件的最佳点。

GPU 云上实际剪枝工作流程

部署已剪枝的 LLMs 需要同时考虑模型层面和基础设施层面的因素。典型的工作流程如下:

  1. 选择基础模型。选择一个适用于目标应用且格式兼容的开源模型(例如 Llama、Mistral、OPT、BLOOM),以便你可以使用(例如 Hugging Face Transformers)。确保密集模型在校准时至少能够放入 VRAM。
  2. 准备校准数据集。SparseGPT 和 Wanda 只需要一个小型数据集,能够反映目标工作负载。如果你在构建代码助手,使用代码提示;如果你在构建支持聊天机器人,使用实际的支持对话。几百个样本就足够了。
  3. 选择稀疏程度和模式。你可以从 20–30% 的小比例开始,以观察效果。40–50% 的中等稀疏度能够带来显著的内存节省,同时质量下降有限。为了硬件加速,可以考虑 2:4 或 4:8 的模式。
  4. 应用剪枝方法。SparseGPT 方法只需对每一层模型运行提供的脚本。算法会触发激活的收集,运行稀疏回归求解器,并可选择性地更新权重。Wanda 则需要你对每个线性投影实现重要性度量。每种方法仅需单个 GPU。运行时间会随着模型大小而线性增长。
  5. 保存稀疏检查点。直接使用合适的格式存储权重。对于非结构化稀疏,你可以保存权重和掩码。对于 2: 4 稀疏,你应使用与 PyTorch 或 TensorRT 兼容的压缩格式。记录诸如模型版本、稀疏比例、校准数据、剪枝方法和数据类型等元数据。
  6. 基准测试修剪后的模型。 在相关数据集或工作负载上测量困惑度和下游任务质量。评估显存使用量、每秒 token 数、首 token 延迟、token 间延迟以及每百万 token 的推理成本。在相同条件下测量稠密和稀疏模型。
  7. 集成到服务堆栈。 使用结构化稀疏时,确保推理引擎能够加载稀疏权重格式并调度稀疏内核。框架扩展如 PyTorch 的 to_ sparse_ semi_ structured 可将 2:4 掩码转换并加速 nn.Linear 层。
  8. 在生产环境中进行监控。 跟踪延迟、吞吐量、内存使用量和质量指标。根据需要调整稀疏比例或将剪枝与量化和 KV‑cache 优化相结合。

Python 示例:为线性层实现 Wanda

以下是一个简化的 PyTorch 函数,它对单个线性层应用 Wanda 剪枝。在实际使用中,您需要将此扩展到模型的所有投影层,并处理结构化模式。

import torch
import torch.nn as nn
@torch.no_grad()
def wanda_prune_linear(
    layer: nn.Linear,
    input_activations: torch.Tensor,
    sparsity: float = 0.5
):
    """
    Apply Wanda-style unstructured pruning to one Linear layer.


    Args:
        layer: PyTorch Linear layer.
        input_activations: Calibration activations with shape
            [batch, seq_len, hidden_dim] or [num_tokens, hidden_dim].
        sparsity: Fraction of weights to prune per output row.


    Returns:
        The pruned layer, modified in-place.
    """
    if not isinstance(layer, nn.Linear):
        raise TypeError("wanda_prune_linear expects an nn.Linear layer.")


    if not 0.0 <= sparsity <= 1.0:
        raise ValueError("sparsity must be between 0 and 1.")


    # Flatten activations to shape [n_tokens, input_dim]
    if input_activations.dim() == 3:
        X = input_activations.reshape(-1, input_activations.shape[-1])
    else:
        X = input_activations


    W = layer.weight


    if X.shape[-1] != W.shape[1]:
        raise ValueError(
            f"Activation dimension {X.shape[-1]} does not match "
            f"layer input dimension {W.shape[1]}."
        )


    # Compute L2 norm of each input dimension
    activation_norm = torch.norm(X, p=2, dim=0)


    # Compute Wanda importance scores: |W_ij| * ||X_j||
    scores = torch.abs(W) * activation_norm.unsqueeze(0)


    # Number of weights to prune per output row
    num_prune = int(W.shape[1] * sparsity)


    if num_prune == 0:
        return layer


    # Build pruning mask
    mask = torch.ones_like(W, dtype=torch.bool)


    for row in range(W.shape[0]):
        prune_indices = torch.topk(
            scores[row],
            k=num_prune,
            largest=False
        ).indices
        mask[row, prune_indices] = False


    # Apply mask in-place
    W.mul_(mask)


    return layer

# Example usage
device = "cuda" if torch.cuda.is_available() else "cpu"

hidden_dim = 4096
linear = nn.Linear(hidden_dim, hidden_dim, bias=False).half().to(device)

calibration_activations = torch.randn(
    4, 128, hidden_dim,
    device=device,
    dtype=torch.float16
)

pruned_layer = wanda_prune_linear(
    linear,
    calibration_activations,
    sparsity=0.5
)

zero_count = torch.sum(pruned_layer.weight == 0).item()
total_count = pruned_layer.weight.numel()

print(f"Sparsity: {zero_count / total_count:.2%}")

这是在 PyTorch 中对单个 nn.Linear 层进行简化 Wanda 风格剪枝的示例。校准激活用于计算每个输入维度的 L2 范数。激活范数与权重的绝对值相乘,得到 Wanda 重要性得分。根据期望的稀疏度比例,对输出的每一行评分最低的权重通过原地乘以二进制掩码进行剪枝。在此示例中,创建了一个半精度线性层。生成随机校准激活。将该层剪枝至 50% 稀疏度,并打印权重中零值的最终百分比。

稀疏模型基准测试:指标与期望

评估剪枝模型需要同时捕捉质量和效率的指标:

  • 困惑度和下游准确率展示了剪枝如何影响语言建模和任务性能。SparseGPT作者报告称,GPT系列模型可以在一次剪枝中达到≥50%的稀疏度,无需任何重新训练,且准确率损失最小。他们表明,OPT-175B和BLOOM-176B均可达到60%的非结构化稀疏度,困惑度增加可忽略不计。
  • VRAM使用量反映内存节省。如果推理栈以压缩格式存储剪枝后的权重并使用稀疏内核,VRAM消耗大约与稀疏度成比例下降。
  • 吞吐量(tokens/second)延迟衡量服务效率。加速需要用稀疏内核替换密集内核;否则,零权重仍会消耗计算。对于半结构化2:4稀疏度,PyTorch’s tutorial展示了在BERT上的A100 GPU可实现1.3×加速。
  • 首 token 延迟令牌间延迟衡量用户体验。较小的内存占用可以降低这些延迟。
  • 每百万 token 的成本 将工程改进与业务价值联系起来。能够将内存减半并提高吞吐量的压缩技术可以显著降低每 token 的成本。
  • 设定现实的期望很重要:50% 的稀疏性并不能保证 2 倍的加速。实际的加速取决于硬件支持、内核实现、批次大小,以及工作负载是主要由权重计算还是 KV‑缓存 操作。结构化稀疏性更容易加速,因为硬件和软件支持特定的模式。非结构化稀疏性通常需要自定义 CUDA 内核或类似 Triton 的框架。

    稀疏推理的基础设施方面

    剪枝不仅仅是一种模型优化技术——它是一个基础设施问题。若干因素决定稀疏性是否能转化为加速。

    • 检查点格式。 在密集张量中存储零权重会浪费存储并降低潜在的内存节省。半结构化稀疏性存储在压缩格式中,其中非零元素随描述其位置的元数据一起存储。像 cusparSELt 这样的库提供针对特定支持的稀疏格式优化的内核。
    • 内核支持。 密集矩阵乘法内核不会跳过零权重。为了获得加速,运行时必须调度能够利用稀疏权重布局的稀疏内核。PyTorch 的 to_ sparse_semi_ structured 方法可以将已经具有受支持半结构化稀疏模式的权重转换为稀疏张量数据类型,从而解锁稀疏内核执行。
    • NVIDIA Ampere 和 Hopper GPU 支持稀疏 张量核心,但仅适用于硬件友好的模式,例如 2:4 稀疏。非结构化稀疏性(即随机剪枝)更难以加速,可能需要通用的稀疏 GEMM 库 或编写自定义内核。此外,除非模型既极其稀疏且运行时已针对稀疏进行优化,否则它通常不会提供显著的加速。
    • 批处理和调度。 稀疏矩阵乘法可能更快,但总体延迟还取决于 KV 缓存管理、批处理、请求调度和内存带宽。在长上下文或高并发工作负载中,KV 缓存可能占据主要内存使用,从而削弱权重剪枝的实际收益。
    • 可观测性。 良好的延迟、吞吐量、GPU 利用率、内存使用和质量的可观测性至关重要。虽然在排行榜基准测试中稀疏化模型可能降低延迟,但可能导致面向用户的任务质量低于生产标准。

    下图展示了只有在模型、稀疏检查点格式、GPU 内核和服务基础设施正确对齐时,剪枝才能提升 LLM 推理速度。它使用一个简单的 2:4 稀疏示例来说明,仅靠剪枝无法获得真实的生产收益。

    何时使用 SparseGPT 和 Wanda

    当您希望在不从头重新训练的情况下降低推理成本时,可以选择 SparseGPT 或 Wanda。使用它们可以将更大的模型塞进更小的 GPU,在单个 GPU 节点上服务更多模型,降低内存占用和 KV 缓存压力。它们能帮助快速测试您的 LLM 的压缩变体,为边缘部署做好模型准备,并提升开源 LLM 部署的经济性。

    如果您的推理引擎无法利用稀疏权重、工作负载大部分时间访问 KV 缓存、剪枝后模型质量下降,或部署硬件不支持稀疏加速,则不要仅依赖剪枝。

    如果在更高稀疏度下保持质量是您的主要目标,请选择 SparseGPT;如果快速实验和低实现复杂度更重要,请选择 Wanda。

    正确的生产问题不仅仅是:“模型是否稀疏?”更好的问题是:“此稀疏模型在保持质量的前提下,是否降低了每个有用 token 的成本?”

    常见问题

    • 什么是 LLM 剪枝? LLM 剪枝是一种压缩技术,通过去除模型中不太重要的权重来实现。其目标是在保持模型质量的同时降低内存使用和推理成本。

    • SparseGPT 和 Wanda 有什么区别? SparseGPT 使用二阶重建来在剪枝后保持层输出。Wanda 使用一种更简单的激活感知得分,基于权重幅度和输入激活范数,使其更易于且更快速地应用。

    • 剪枝会自动让 LLM 变快吗? 不会。只有当推理栈能够通过稀疏格式、稀疏内核和兼容硬件利用稀疏权重时,剪枝才能提升速度。如果密集内核仍然处理零权重,延迟可能不会改善。

    • 何时应该选择 SparseGPT? 当主要目标是在更高稀疏度水平下保持质量时,应选择 SparseGPT。虽然它更复杂,但它通过重建和权重补偿来保持模型行为的设计。

    • 何时应该选择 Wanda? 当快速实验、简单性和低实现复杂度更重要时,应选择 Wanda。它是在投入更复杂优化之前快速测试剪枝对模型影响的强基线。

    结论

    SparseGPT 和 Wanda 都表明,我们可以在训练后无需重新训练的情况下对大型语言模型进行剪枝。SparseGPT 使用基于重建的复杂度量及二阶近似,以在保持准确性的同时实现大规模剪枝。Wanda 采用更简单的激活感知方法,将权重乘以输入范数激活,以最小的工程成本实现快速剪枝。两个项目都产生了稀疏模型,能够减轻内存压力,(在具备合适内核和硬件的情况下)提升推理吞吐量并降低延迟。

    然而,剪枝并不是唯一的考虑因素。只有在系统级别支持稀疏格式、自定义稀疏内核、智能内存优化以及诸如量化和 KV‑cache 管理等其他技术的情况下,才能实现加速和成本节约。在生产环境中部署的推理栈很可能会采用多种策略的组合——剪枝、量化、缓存、批处理、推测解码、路由——以最低可能的成本提供响应式 AI 服务。在那种未来中,剪枝将成为 LLM 优化流水线的标准部分:不仅仅是研究上的新奇点,而是一种实用工具,用于降低每个 token 的成本并高效扩展 AI 服务。

    参考文献

    ——

    🧑‍💻

    zhirenhun

    一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。