标签: AI

  • 解码加速:Flash-Decoding 让长文本推理更快

    大型语言模型(LLM)如 ChatGPT 和 Llama 近年来备受关注,但它们的运行成本依然高昂。即使生成单个回复可能只需要 0.01 美元(在 AWS 上使用 8xA100 实例运行几秒钟),但当扩展到数十亿用户时,成本会迅速增加,因为这些用户每天可能与 LLM 进行多次交互。一些用例的成本更高,例如代码自动补全,因为每次输入新字符时都会运行。随着 LLM 应用的增加,即使生成时间略微提高效率,也会产生巨大的影响。

    LLM 推理(或“解码”)是一个迭代过程:一次生成一个词元。生成包含 N 个词元的完整句子需要对模型进行 N 次前向传递。幸运的是,可以缓存先前计算的词元:这意味着单个生成步骤不依赖于上下文长度,除了一个操作:注意力机制。该操作的计算量随着上下文长度的增加而迅速增长。

    LLM 的一些重要新兴用例利用了长上下文。有了更长的上下文,LLM 可以推理更长的文档,例如对文档进行摘要或回答有关文档的问题,它们可以跟踪更长的对话,甚至在编写代码之前处理整个代码库。例如,大多数 LLM 在 2022 年的上下文长度最多为 2k(GPT-3),但现在我们拥有上下文长度扩展到 32k(Llama-2-32k)甚至 100k(CodeLlama)的开源 LLM。在这种情况下,注意力机制在推理过程中占用了大量时间。

    当扩展批次大小维度时,即使上下文相对较短,注意力机制也会成为瓶颈。这是因为要读取的内存量随着批次大小维度而扩展,而它仅取决于模型大小。

    我们提出了一种名为 Flash-Decoding 的技术,它可以显著加快推理过程中的注意力机制,对于非常长的序列,可以使生成速度提高 8 倍。主要思想是尽可能快地并行加载键和值,然后分别重新缩放和组合结果以保持正确的注意力输出。

    解码的多头注意力机制

    在解码过程中,每个新生成的词元都需要关注所有先前的词元,以计算:

    softmax(queries @ keys.transpose) @ values

    此操作已在训练情况下使用 FlashAttention(最近的 v1 和 v2 版本)进行了优化,其中瓶颈是读取和写入中间结果(例如 Q @ K^T. 的内存带宽。然而,这些优化不直接适用于推理情况,因为瓶颈不同。对于训练,FlashAttention 在批次大小和查询长度维度上并行化。在推理过程中,查询长度通常为 1:这意味着如果批次大小小于 GPU 上的流式多处理器数量(A100 为 108),则操作将仅使用 GPU 的一小部分!当使用长上下文时尤其如此,因为它需要更小的批次大小才能适应 GPU 内存。如果批次大小为 1,FlashAttention 将使用不到 GPU 的 1%!

    FlashAttention 仅在查询块和批次大小上并行化,无法在解码过程中占用整个 GPU。

    注意力机制也可以使用矩阵乘法原语来完成,而无需使用 FlashAttention。在这种情况下,操作会完全占用 GPU,但会启动许多内核来写入和读取中间结果,这并非最佳选择。

    解码的更快注意力机制:Flash-Decoding

    我们新的方法 Flash-Decoding 基于 FlashAttention,并添加了一个新的并行化维度:键/值序列长度。它结合了上述两种方法的优点。与 FlashAttention 一样,它存储到全局内存的额外数据很少,但即使批次大小很小,只要上下文长度足够长,它也能充分利用 GPU。

    Flash-Decoding 也在键和值上并行化,但需要一个小的最终归约步骤。

    Flash-Decoding 分三个步骤进行:

    1. 首先,我们将键/值分成更小的块。
    2. 我们使用 FlashAttention 并行计算查询与每个块的注意力。我们还为每行和每个块写入一个额外的标量:注意力值的 log-sum-exp。
    3. 最后,我们使用 log-sum-exp 来缩放每个块的贡献,通过对所有块进行归约来计算实际输出。

    所有这些都是可能的,因为注意力/softmax 可以迭代计算。在 Flash-Decoding 中,它在两个级别上使用:在块内(类似于 FlashAttention),以及在块之间进行最终归约。

    实际上,步骤 (1) 不涉及任何 GPU 操作,因为键/值块是完整键/值张量的视图。然后我们有两个独立的内核分别执行 (2) 和 (3)。

    CodeLlama 34B 的基准测试

    为了验证这种方法,我们对 CodeLLaMa-34b 的解码吞吐量进行了基准测试。该模型与 Llama 2 具有相同的架构,更一般而言,结果应该可以推广到许多 LLM。我们测量了不同序列长度(从 512 到 64k)下的解码速度(tok/s),并比较了几种计算注意力机制的方法:

    • Pytorch:使用纯 PyTorch 原语(不使用 FlashAttention)运行注意力机制。
    • FlashAttention v2。
    • FasterTransformer:使用 FasterTransformer 注意力内核。
    • Flash-Decoding。
    • 以及一个上限,计算为读取整个模型以及 KV 缓存所需的时间。

    Flash-Decoding 在解码速度方面为非常长的序列带来了高达 8 倍的加速,并且比其他方法的扩展性更好。

    所有方法在小提示情况下表现相似,但随着序列长度从 512 增加到 64k,扩展性都很差,除了 Flash-Decoding。在这种情况下(批次大小为 1),使用 Flash-Decoding,扩展序列长度对生成速度几乎没有影响。

    组件级微基准测试

    我们还在 A100 上对不同序列长度和批次大小的缩放多头注意力机制进行了微基准测试,输入为 f16。我们将批次大小设置为 1,并使用 16 个维度为 128 的查询头,用于 2 个键/值头(分组查询注意力),这与在 4 个 GPU 上运行 CodeLLaMa-34b 时使用的维度相匹配。

    设置算法运行时间(us)
    B=256, seqlen=256PyTorch Eager3058.6
    B=256, seqlen=256Flash-Attention v2.0.9390.5
    B=256, seqlen=256Flash-Decoding63.4
    B=128, seqlen=512PyTorch Eager3151.4
    B=128, seqlen=512Flash-Attention v2.0.9366.3
    B=128, seqlen=512Flash-Decoding67.7
    B=64, seqlen=1024PyTorch Eager3160.4
    B=64, seqlen=1024Flash-Attention v2.0.9364.8
    B=64, seqlen=1024Flash-Decoding77.7
    B=32, seqlen=2048PyTorch Eager3158.3
    B=32, seqlen=2048Flash-Attention v2.0.9352
    B=32, seqlen=2048Flash-Decoding58.5
    B=16, seqlen=4096PyTorch Eager3157
    B=16, seqlen=4096Flash-Attention v2.0.9401.7
    B=16, seqlen=4096Flash-Decoding57
    B=8, seqlen=8192PyTorch Eager3173.1
    B=8, seqlen=8192Flash-Attention v2.0.9529.2
    B=8, seqlen=8192Flash-Decoding56.4
    B=4, seqlen=16384PyTorch Eager3223
    B=4, seqlen=16384Flash-Attention v2.0.9582.7
    B=4, seqlen=16384Flash-Decoding58.2
    B=2, seqlen=32768PyTorch Eager3224.1
    B=2, seqlen=32768Flash-Attention v2.0.91156.1
    B=2, seqlen=32768Flash-Decoding60.3
    B=1, seqlen=65536PyTorch Eager1335.6
    B=1, seqlen=65536Flash-Attention v2.0.92300.6
    B=1, seqlen=65536Flash-Decoding64.4
    B=1, seqlen=131072PyTorch Eager2664
    B=1, seqlen=131072Flash-Attention v2.0.94592.2
    B=1, seqlen=131072Flash-Decoding106.6

    多头注意力机制的微基准测试,运行时间为 us。Flash-Decoding 在序列长度扩展到 64k 时,运行时间几乎保持不变。

    先前测量的端到端高达 8 倍的加速是可能的,因为注意力机制本身比 FlashAttention 快 50 倍。在序列长度达到 32k 之前,注意力时间大致保持不变,因为 Flash-Decoding 设法充分利用了 GPU。

    如何使用 Flash-Decoding

    Flash-decoding 可在以下位置使用:

    • FlashAttention 包,从 2.2 版本开始。
    • xFormers,从 0.0.22 版本开始,通过 xformers.ops.memory_efficient_attention。调度器将根据问题大小自动使用 Flash-Decoding 或 FlashAttention 方法。当这些方法不受支持时,它可以调度到一个高效的 triton 内核,该内核实现了 Flash-Decoding 算法。

    LLaMa v2 / CodeLLaMa 的完整解码示例可在 FlashAttention 存储库 这里 和 xFormers 存储库 这里 找到。我们还提供了一个 LLaMa v1/v2 模型的最小高效解码代码示例,旨在快速、易于阅读、具有教育意义和可修改性。

    致谢

    感谢 Erich Elsen、Ashish Vaswani 和 Michaël Benesty 建议了将 KV 缓存加载拆分的这个想法。我们要感谢 Jeremy Reizenstein、Patrick Labatut 和 Andrew Tulloch 的宝贵讨论,以及 Quentin Carbonneaux 为 xFormers 提供高效的解码示例。我们还要感谢 Geeta Chauhan 和 Gregory Chanan 在写作方面提供的帮助,以及更广泛地为将本文发表在 PyTorch 博客上做出的贡献。

    参考文献:

  • 让你的深度学习模型“Brrr”起来:从基本原理出发

    你是否想要提升深度学习模型的性能?面对这个问题,很多人会习惯性地使用一些“秘诀”,比如“使用就地操作!将梯度设置为 None!安装 PyTorch 1.10.0,但不要安装 1.10.1!”。

    这种“灵机一动”式的优化方法虽然看似有效,但实际上却缺乏理论基础。深度学习模型的性能优化并非炼金术,而是需要从基本原理出发进行分析。

    理解性能瓶颈

    深度学习模型的性能优化可以从三个方面进行考虑:

    • 计算 (Compute): GPU 上执行浮点运算 (FLOPS) 所花费的时间。
    • 内存 (Memory): 在 GPU 内传输张量所花费的时间。
    • 开销 (Overhead): 其他所有时间。

    就像训练机器学习模型一样,了解模型所处的性能瓶颈可以帮助我们找到最有效的优化方法。例如,如果你的模型主要受限于内存带宽,那么提升 GPU 的 FLOPS 性能将毫无用处。相反,如果你的模型主要受限于计算能力,那么将模型逻辑重写为 C++ 代码来降低开销也无济于事。

    计算:GPU 的核心能力

    从某种程度上来说,优化深度学习系统就是最大化模型在计算受限状态下运行的时间。你为那些强大的 GPU 付出了高昂的代价,理所当然地希望它们能够发挥出全部的计算能力。然而,为了充分利用 GPU 的矩阵乘法能力,我们需要减少其他方面的开销,例如内存传输和系统开销。

    为什么我们更关注最大化计算能力,而不是内存带宽?原因很简单:我们可以降低开销或内存成本,但我们无法在不改变实际操作的情况下减少计算量。

    计算与内存带宽的矛盾

    更糟糕的是,计算能力的增长速度远超内存带宽。下表展示了 CPU FLOPS 和内存带宽的翻倍时间:

    组件翻倍时间
    CPU FLOPS18 个月
    内存带宽24 个月

    我们可以将计算能力想象成一个工厂。我们向工厂发送指令(开销),并提供原材料(内存带宽),以确保工厂高效运转(计算)。

    如果工厂的效率提升速度快于我们提供原材料的速度,那么工厂将难以达到峰值效率。

    即使工厂的规模(FLOPS)翻倍,如果我们的带宽跟不上,那么性能也不会翻倍。

    这种计算能力增长速度快于内存带宽的趋势,一方面意味着机器学习系统工程师将长期拥有稳定的工作,另一方面也使得理解性能瓶颈变得更加重要。

    现代 GPU 的特殊性

    现代机器学习加速器都拥有专门用于矩阵乘法的硬件,例如英伟达的“Tensor Cores”。

    如果你没有进行矩阵乘法,那么你只能达到 19.5 teraflops,而不是标称的 312 teraflops。需要注意的是,这并非 GPU 独有,事实上,TPU 的通用性甚至比 GPU 更低。

    GPU 在非矩阵乘法运算方面速度较慢,这似乎是一个问题。那么,其他运算,例如层归一化或激活函数呢?事实是,这些运算在 FLOPS 方面几乎可以忽略不计。例如,让我们看一下这篇论文中关于 BERT 不同运算类型 FLOPS 数量的表格,其中“张量收缩”= 矩阵乘法。

    运算类型FLOPS 占比
    张量收缩99.8%
    归一化0.1%
    点运算0.1%

    可以看到,非矩阵乘法运算只占总 FLOPS 的 0.2%,因此 GPU 在非矩阵乘法运算方面速度慢 15 倍并不重要。

    内存带宽:数据传输的瓶颈

    然而,在实际应用中,非矩阵乘法运算却往往比预期花费更多时间。罪魁祸首通常是数据在工厂和仓库之间传输的时间,也就是内存带宽成本。

    内存带宽成本是指将数据从一个地方移动到另一个地方所花费的成本。这可能包括将数据从 CPU 移动到 GPU,从一个节点移动到另一个节点,甚至从 CUDA 全局内存移动到 CUDA 共享内存。其中,最后一种情况通常被称为“带宽成本”或“内存带宽成本”。

    我们可以再次使用工厂的比喻来理解内存带宽成本。

    虽然工厂是进行实际工作的场所,但它并不适合作为大规模存储单元。主要原因是,由于我们在工厂进行实际工作,因此所有存储都针对快速使用进行了优化(SRAM),而不是拥有大量的存储空间。

    那么,我们应该在哪里存储实际结果和原材料呢?典型的做法是建立一个仓库,可能位于土地便宜且空间充足的地方(DRAM)。然后,我们可以将物资运送到工厂和从工厂运出(内存带宽)。

    将物资运送到计算单元和从计算单元运出的成本就是所谓的“内存带宽”成本。顺便说一下,你的 GPU 的 DRAM 会显示在 nvidia-smi 中,它是你遇到“CUDA 内存不足”错误的主要原因。

    需要注意的是,每次执行 GPU 内核时,都需要将数据从 GPU 的 DRAM(即仓库)中读取出来,并在执行完内核后将结果写入 DRAM。

    现在,想象一下执行像 torch.cos 这样的单目运算会发生什么。我们需要将数据从存储单元运送到仓库,然后对每条数据进行少量计算,最后将数据运回存储单元。运输数据的成本非常高。因此,我们几乎所有时间都花在了运输数据上,而不是实际的计算本身。

    由于我们所有时间都花在了内存带宽上,因此这种运算被称为内存受限运算,这意味着我们没有花太多时间进行计算。

    操作融合:优化内存带宽

    那么,我们该如何解决这个问题呢?让我们看一下一系列运算的执行过程。

    一系列点运算的执行过程可能如下所示:

    这种安排非常愚蠢。为什么我们要将相同的数据反复发送到全局内存,然后再发送回计算单元?我们应该将数据保留在工厂中,执行所有计算,然后再将其发送回存储单元。

    我们可以通过操作融合来解决这个问题。操作融合是深度学习编译器中最重要的优化方法。简单来说,与其将数据写入全局内存,然后再读取回来,不如将多个计算合并在一起执行,从而避免额外的内存访问。

    例如,如果我们执行 x.cos().cos(),通常需要进行 4 次全局读写操作。

    x1 = x.cos()  # 从全局内存中读取 x,写入 x1
    x2 = x1.cos()  # 从全局内存中读取 x1,写入 x2

    但是,使用操作融合,我们只需要进行 2 次全局内存读写操作!因此,操作融合可以将速度提高 2 倍。

    x2 = x.cos().cos()  # 从全局内存中读取 x,写入 x2

    操作融合可以显著提高性能,但它也有一些限制。首先,GPU 需要在执行当前操作时知道下一步要执行的操作。因此,我们无法在 Eager 模式下执行操作融合,因为 PyTorch 在 Eager 模式下会逐个执行操作。其次,我们需要为此生成 CUDA 代码,这会带来新的挑战。

    并非所有操作融合都像点运算那样简单。你可以将点运算融合到约简运算中,或者将点运算融合到矩阵乘法中。甚至矩阵乘法本身也可以看作是广播乘法和约简的融合。

    如果你有兴趣编写自定义 CUDA 内核,那么操作融合将是你最受益的优化方法。任何两个 PyTorch 操作都存在融合的可能性,从而节省了在它们之间读写全局内存的内存带宽成本。此外,许多现有的编译器可以执行“简单的”融合,例如 NVFuser 和 XLA。然而,自动系统无法与人类的智慧相提并论,因此,如果你想尝试编写一些自定义 CUDA 内核,那么 Triton 是一个不错的起点。

    最后,操作融合会带来一些意想不到的结果。例如,融合后的 x.cos().cos() 的执行时间几乎与单独调用 x.cos() 的时间相同。这就是为什么激活函数的成本几乎都相同,尽管 gelu 明显比 relu 包含更多的操作。

    这一事实导致了重计算/激活检查点的一些有趣结果。本质上,进行额外的重计算可能会导致更少的内存带宽,从而减少运行时间。因此,我们可以通过重计算来降低内存和运行时间,我们在 AOTAutograd 中利用它构建了一个巧妙的最小割优化过程。你可以在此处阅读更多相关内容(也可能在未来的博客文章中进行介绍!)。

    如何判断内存带宽受限

    在判断你的操作是否内存带宽受限时,计算器可以起到很大的作用。

    对于简单的操作,我们可以直接计算内存带宽。例如,A100 拥有 1.5 terabytes/second 的全局内存带宽,可以执行 19.5 teraflops/second 的计算。因此,如果你使用 32 位浮点数(即 4 字节),那么你可以在 GPU 执行 20 万亿次操作的同时加载 4000 亿个数字。此外,为了执行简单的单目运算(例如将张量乘以 2),实际上需要将张量写回全局内存。

    因此,除非你的单目运算执行了大约一百次操作,否则你会花费更多时间执行内存访问,而不是实际的计算。

    借助 NVFuser 这样的融合编译器,我们可以很容易地测量内存带宽。你可以在此处查看 Colab 代码。

    如果我们使用 PyTorch 函数,例如:

    def f(x: Tensor[N]):
        for _ in range(repeat):
            x = x * 2
        return x

    并使用融合编译器对其进行基准测试,那么我们可以计算出不同 repeat 值下达成的 FLOPS 和内存带宽。增加 repeat 是增加计算量而不增加内存访问次数的一种简单方法,这也被称为增加计算强度。

    具体来说,假设我们对这段代码进行基准测试,并找到每秒执行的迭代次数。那么,作为 N. 张量大小)的函数,我们将执行 2*N 次内存访问,以及 N * repeat 次 FLOP。因此,达成的内存带宽将为 bytes_per_elem * 2 * N * itrs_per_second,达成的 FLOPS 将为 N * repeat * itrs_per_second。

    现在,让我们绘制运行时间、FLOPS 和达成的内存带宽作为计算强度的函数。注意,所有内容都以对数对数刻度显示。

    首先,请注意,直到我们执行 64 次乘法运算,运行时间才明显增加。这意味着,在此之前,我们主要受限于内存带宽,我们的计算能力大部分处于闲置状态。

    因此,我们最初达成的 FLOPS 只有 0.2 teraflops。随着我们使计算强度翻倍,这个数字线性增长,直到我们接近 9.75 teraflops 的峰值 [1]。一旦我们接近峰值 teraflops,我们就认为是“计算受限”。

    最后,你可以看到,我们达成的内存带宽最初接近峰值,随着我们增加计算强度,它开始下降。这正是我们所期望的,因为我们花费了越来越多的时间执行实际计算,而不是访问内存。

    在这种情况下,很容易看出我们何时是计算受限的,何时是内存带宽受限的。对于 repeat < 32,我们饱和了内存带宽,而我们的计算能力未得到充分利用。相反,一旦 repeat > 64,我们看到我们的计算能力已经饱和(即接近峰值 FLOPS),而我们利用的内存带宽开始下降。

    对于更大的系统,我们通常很难判断是计算受限还是内存带宽受限,因为它们通常包含计算受限和内存带宽受限的组件。

    衡量计算受限程度的一种常见方法是测量达成的 FLOPS 占峰值 FLOPS 的百分比。例如,如果你达成了峰值 FLOPS 的 80%,那么你就知道你至少是 80% 的计算受限,这已经相当不错了!你剩下的时间可能都花在了执行内存带宽操作上。[2]

    然而,除了内存带宽成本之外,还有一件事可能会导致你的 GPU 无法“Brrr”起来。

    开销:系统瓶颈

    开销是指你的代码花费在传输张量或计算之外的所有时间。例如,在 Python 解释器中花费的时间?开销。在 PyTorch 框架中花费的时间?开销。启动 CUDA 内核(但没有执行它们)花费的时间?也是开销。

    开销之所以是一个棘手的问题,主要是因为现代 GPU 非常快。A100 每秒可以执行 312 万亿次浮点运算(312 TeraFLOPS)。相比之下,Python 非常慢。在本地进行基准测试,Python 每秒可以执行 3200 万次加法运算。

    这意味着,在 Python 执行一次 FLOP 的时间里,A100 可以执行 975 万次 FLOPS。

    更糟糕的是,Python 解释器并不是开销的唯一来源,像 PyTorch 这样的框架在到达实际内核之前也有很多层调度。如果你使用 PyTorch 执行相同的实验,我们每秒只能执行 28 万次操作。当然,PyTorch 的设计目的不是处理微小的张量,但是,如果你使用的是微小的张量(例如在科学计算中),你可能会发现 PyTorch 比 C++ 慢得多。

    例如,看一下 PyTorch 执行一次加法的火焰图配置文件。那个方框?那是执行实际计算的部分。其他所有部分都是纯粹的开销。

    鉴于此,你可能会惊讶于为什么有人会使用 PyTorch,但请记住,现代深度学习模型通常会执行大规模运算。此外,像 PyTorch 这样的框架是异步执行的。也就是说,当 PyTorch 运行 CUDA 内核时,它可以继续运行并在其后面排队更多 CUDA 内核。因此,只要 PyTorch 可以“领先于”CUDA 内核,大部分框架开销就会完全隐藏起来!

    如果我们的 GPU 操作足够大,那么我们的 CPU 可以领先于 GPU(因此 CPU 开销无关紧要)。另一方面,如果我们的 GPU 操作太小,那么我们的 GPU 将大部分时间都浪费在昂贵的“纸镇”上。

    那么,如何判断你是否处于这种状态呢?由于开销通常不会随着问题规模的增加而增加(而计算和内存会增加),因此判断开销受限的最简单方法是增加数据的规模。如果运行时间没有按比例增加,那么你就是开销受限的。例如,如果你将批次大小增加一倍,但运行时间只增加了 10%,那么你很可能是开销受限的。[3]

    另一种方法是使用 PyTorch 分析器。在这里,粉红色的线实际上显示了 CPU 内核与 GPU 内核的匹配情况。

    GPU 上有很多间隙,因为它正在等待 CPU 开销

    我们的 CPU 运行速度远远超过 GPU

    另一个补充说明 – nvidia-smi 中的“GPU-Util”(不是“Volatile GPU-Util”)条目基本上是测量底部的行中实际运行 GPU 内核的百分比。因此,这也是评估开销的一个好方法。

    开销存在的主要原因是像 PyTorch 这样的框架具有很大的灵活性。本质上,需要花费大量时间来“弄清楚要做什么”。

    这可能来自 Python(查找属性或调度到正确的函数)或 PyTorch 中的代码(PyTorch 的所有调度器)。例如,当你执行 a + b 时,需要执行以下步骤。

    Python 需要查找 a 上的 add 调度到什么。
    PyTorch 需要确定张量的许多属性(例如数据类型、设备以及是否需要自动微分)以确定要调用哪个内核。
    PyTorch 需要实际启动内核。

    从根本上说,这种开销来自于在每一步都能够执行不同操作的灵活性。如果你不需要这种灵活性,那么解决这种灵活性的方法之一是将其追踪出来,例如使用 jit.trace、FX 或 jax.jit。或者,你也可以在更低级别使用像 CUDA Graphs 这样的工具来实现。

    不幸的是,这样做会失去灵活性。我期待的一种能够兼顾两者的方法是编写类似于“真正的”JIT 的工具,通过在 VM 级别进行内省来实现。有关详细信息,请参阅 TorchDynamo。

    总结

    如果你想加速深度学习系统,最重要的是要了解模型的瓶颈是什么。瓶颈决定了加速系统的最佳方法。

    我经常看到研究人员和其他对加速 PyTorch 代码感兴趣的人盲目地尝试各种方法,而没有了解模型所处的性能瓶颈。

    性能瓶颈可行的解决方案
    开销受限追踪、操作融合、不要使用 Python、真正的 JIT :^)
    带宽受限操作融合
    计算受限使用 Tensor Cores、给英伟达更多钱

    当然,可以说,用户需要考虑这些问题本身就反映了框架的失败。PyTorch 的编译器或分析 API 并不总是那么容易使用,尽管这是一个积极的关注领域。

    无论如何,我发现理解系统的基本原理总是很有用,希望这对你也有帮助。

    PS:如果你喜欢这篇文章,我将在 thonking.ai 上发布我未来的大部分文章。

    致谢

    感谢 Emily Shen、Qian Huang 和 EleutherAI 的成员阅读这篇博文的早期草稿并提供反馈。

    BibTeX 引用

    @article{he2022brrrrfromfirstprinciples,
      author={Horace He},
      title={Making Deep Learning Go Brrrr From First Principles},
      year={2022},
      url={https://horace.io/brrr_intro.html},
    }

    [1] 这可能与规格说明书上显示的 19.5 teraflops 不同。原因是 GPU 拥有更多专门用于融合乘加 (FMA) 指令的硬件。因此,对于完全通用的计算,A100 实际上只能达到 9.75 teraflops。 ↩︎

    [2] 现在在 PyTorch 中以一种很好的方式计算 FLOPS 的方法有很多,请参阅 https://dev-discuss.pytorch.org/t/the-ideal-pytorch-flop-counter-with-torch-dispatch/505 ↩︎

  • 大语言模型的温度、top_k等超参数

    AI大语言模型是一种强大的工具,可以用来生成各种文本,比如故事、新闻和对话。但是,为了让这些生成的文本更有趣、多样,我们需要调整一些参数,这些参数被称为超参数。

    两个重要的超参数是温度(Temperature)和top_k。它们对生成的文本的多样性、创造性和可控性有很大影响。

    首先,让我们来了解一下温度(Temperature)参数。这个参数用来调整模型生成文本时的概率分布。当温度较低时(接近0),模型倾向于选择概率最高的词,这样生成的文本会比较稳定和可预测,但可能会缺乏多样性。相反,当温度较高时(大于1),模型更有可能选择概率较低的词,这样生成的文本会更加多样化和创造性,但可能会有一些不合适的词出现。

    接下来是top_k参数,它用来限制模型在生成下一个词时考虑的候选词的范围。当top_k较低(比如1-10)时,模型只会考虑概率最高的几个词,这有助于生成连贯和相关的文本,但可能会减少多样性。而当top_k较高(比如100或更多)时,模型会考虑更多的候选词,这样可以增加文本的多样性,但也可能会引入一些不相关或不连贯的词。

    在实际应用中,我们需要根据具体任务和所需的文本特性来调整这些超参数。例如,如果我们想要生成既有创意又保持一定连贯性的文本,可以选择一个中等的温度值和一个较高的top_k值。相反,如果我们需要生成高度可预测和精确的文本,可以选择一个较低的温度值和一个较低的top_k值。

    除了温度和top_k参数,还有许多其他超参数会影响AI大语言模型的性能和生成文本的质量。这些超参数包括学习率、批次大小、序列长度、隐藏层大小、层数、dropout比率、优化器、权重衰减、注意力机制参数、微调学习率和步长等。调整这些超参数需要进行实验和测试,找到最佳的参数设置,以满足特定的应用需求。

    超参数调优是一个复杂的任务,通常需要使用不同的方法和工具来搜索最佳的参数组合。一些常用的方法包括网格搜索、随机搜索和自动化超参数优化工具。在进行超参数调优时,我们需要考虑任务的特点和可用资源,选择合适的方法和工具,并记录和分析实验结果,以便进行进一步的优化和复用最佳的模型配置。

    总之,调整超参数对于AI大语言模型生成文本的质量和多样性非常重要。温度和top_k是两个关键的超参数,同时还有其他一些重要的超参数需要进行调优。根据任务需求和资源限制,我们可以选择合适的方法和工具来优化这些超参数,以获得最佳的文本生成效果。


    玩转大语言模型:温度、top_k 等超参数揭秘

    你是否好奇,为什么同一个大语言模型,有时能写出妙笔生花的诗歌,有时却只能生成平淡无奇的文字?答案就在于模型的超参数!这些看似神秘的数字,其实掌握着模型输出的随机性和多样性,就像调味师一样,为文本增添不同的风味。

    1. 温度:让文字更“热”或更“冷”

    想象一下,你正在用一个大语言模型写诗。你希望它能迸发出灵感的火花,写出充满意境的诗句。这时,你就可以调整“温度”参数。

    • 温度 = 1: 模型就像一个循规蹈矩的诗人,只会写出最常见的诗句,缺乏新意。
    • 温度 < 1: 模型变得更加保守,写出的诗句更加平稳,适合写一些需要逻辑严谨的文本。
    • 温度 > 1: 模型变得更加大胆,写出的诗句更加跳脱,充满想象力,适合写一些需要创意的文本。

    2. top_k:选择词语的“圈子”

    top_k 参数就像一个筛选器,它决定了模型在预测下一个词语时,会考虑多少个候选词。

    • top_k = 1: 模型只考虑概率最高的词语,就像一个固执己见的诗人,只写自己最喜欢的词语。
    • top_k > 1: 模型会考虑多个候选词,就像一个博览群书的诗人,可以从更多词语中选择,写出更加丰富多彩的诗句。

    3. top_p:概率的“门槛”

    top_p 参数与 top_k 类似,但它更注重候选词的概率总和。

    • top_p = 1: 模型会考虑所有候选词,就像一个无所不包的诗人,会使用各种各样的词语。
    • top_p < 1: 模型只考虑概率总和达到 top_p 的候选词,就像一个精挑细选的诗人,只使用最合适的词语。

    4. 随机种子:让文本“重现”或“随机”

    随机种子就像一个种子,它决定了模型输出的随机性。

    • 相同的随机种子: 模型每次运行都会生成相同的文本,就像一个固定的诗人,每次都会写出相同的诗句。
    • 不同的随机种子: 模型每次运行都会生成不同的文本,就像一个充满灵感的诗人,每次都会写出不同的诗句。

    5. 其他参数:文本的“修饰”

    除了以上参数,还有其他参数可以影响文本的输出,例如:

    • 最大长度: 控制文本的最大长度,就像给诗人设定了诗歌的字数限制。
    • 重复惩罚: 惩罚模型重复使用相同的词语,就像给诗人设定了词语使用的限制。
    • 长度惩罚: 惩罚模型生成过长或过短的文本,就像给诗人设定了诗歌的长度限制。

    总结:

    温度、top_k、top_p 等超参数就像大语言模型的“调味料”,通过调整这些参数,我们可以生成不同风格、不同随机性的文本,满足不同的应用场景需求。

    应用场景举例:

    • 创作诗歌: 可以使用较高的温度和较大的 top_k 值,以生成更具创造性的诗歌。
    • 翻译文章: 可以使用较低的温度和较小的 top_k 值,以生成更准确的翻译。
    • 编写代码: 可以使用较低的温度和较小的 top_k 值,以生成更可靠的代码。

    注意: 这些参数的最佳值取决于具体的应用场景和模型。需要根据实际情况进行调整和测试。

    参考文献:


  • LLM 推理系列:深入解析 KV 缓存

    在之前的文章中,我们介绍了 KV 缓存,这是一种常见的 LLM 推理过程优化方法,它可以使(自)注意力机制的计算需求随总序列长度(提示 + 生成的完成)线性增长,而不是二次增长。

    更具体地说,KV 缓存通过在生成过程中计算时将过去标记的键和值张量存储(“缓存”)在 GPU 内存中,来避免在每个生成步骤中重新计算这些张量。

    KV 缓存是一种折衷方案:我们用内存换取计算。在这篇文章中,我们将了解 KV 缓存可以增长到多大,它会带来什么挑战,以及解决这些挑战最常用的策略。

    KV 缓存到底可以增长到多大?

    这很简单:对于批次中每个序列的每个标记,我们需要为每个注意力层的每个注意力头存储两个向量张量(一个键张量和一个值张量),每个张量的尺寸为 $d_{head}$。每个张量参数所需的存储空间取决于精度:全精度(FP32)为 4 字节/参数,半精度(BF16,FP16)为 2 字节/参数,8 位数据类型(INT8,FP8)为 1 字节/参数,等等。

    假设 $b$ 为批次大小,$t$ 为总序列长度(提示 + 完成),$n_{layers}$ 为解码器块/注意力层的数量,$n_{heads}$ 为每个注意力层的注意力头的数量,$d_{head}$ 为注意力层的隐藏维度,$p_a$ 为精度。多头注意力(MHA)模型的 KV 缓存的每个标记的内存消耗(以字节为单位)为:

    $$
    KV_cache_size_per_token = 2 \cdot n_{heads} \cdot d_{head} \cdot p_a
    $$

    注意: 我们提醒,在 MHA 模型中,$n_{heads} \cdot d_{head} = d_{model}$,但我们不会使用它来简化上面的公式。

    因此,KV 缓存的总大小(以字节为单位)为:

    $$
    KV_cache_size = b \cdot t \cdot KV_cache_size_per_token
    $$

    KV 缓存的第一个挑战出现了:它随着批次大小线性增长,更重要的是随着总序列长度线性增长。由于它随着总序列长度增长,因此 KV 缓存的大小实际上是无界的,而我们的 GPU 内存显然是有限的。更糟糕的是,由于总序列长度无法提前知道,因此 KV 缓存的内存需求也是未知的,这使得内存管理特别具有挑战性。

    让我们看看一些流行的 MHA 模型(表 1)的数字,包括 Meta 的 Llama-2 [1] 和 OPT [2],MosaicML 的 MPT [3] 和 BigScience 的 BLOOM [4]:

    模型参数层数头数隐藏维度
    Llama-2–7B7B32324096
    OPT-1.3B1.3B2416768
    MPT-7B7B24324096
    BLOOM-176B176B608012288

    表 1 – 流行的多头注意力 (MHA) 模型规格

    假设参数存储在半精度(FP16,BF16)中,并选择一个较小的模型(Llama-2–7B. 和一个较大的模型(BLOOM-176B)。对于 Llama-2–7B(分别为 BLOOM-176B),KV 缓存的内存消耗约为 0.5MB/标记(分别为 4MB/标记)。

    让我们关注 Llama-2–7B. 使用半精度,加载模型权重消耗约 14GB 内存,与缓存 28k 个标记的键和值相同。28k 个标记可能对应于 56 个长度为 512 的序列的批次,这并不算特别极端。

    从上面的数字可以看出,KV 缓存的内存消耗会变得非常大,甚至可能超过加载大型序列模型权重所需的内存量。

    现在让我们将这些数字与常用的 NVIDIA 数据中心 GPU 的内存容量进行比较(表 2):

    GPU内存
    A1024GB
    A10040GB
    H10080GB
    H20096GB

    表 2 – 常用于训练和/或服务 LLM 的 NVIDIA 数据中心 GPU 规格

    让我们选择性价比比较高的 A10 GPU,坚持使用 Llama-2–7B 并计算最大 KV 缓存容量。加载模型权重后,还有 24-2×7=10 GB 可用于 KV 缓存,即总容量约为 20k 个标记,包括提示,这显然不允许在处理或生成长序列时,特别是当处理或生成长序列时,无法处理大量并发请求。

    我们现在了解到,KV 缓存阻止我们处理或生成非常长的序列(即阻碍长上下文窗口)和/或处理大型批次,因此无法最大限度地提高硬件效率。

    从这个角度来看,最大限度地提高处理能力意味着为 KV 缓存留出尽可能多的空间,这可以通过以下方法实现:

    • 减少模型权重的内存占用(权重量化)
    • 减少 KV 缓存的内存占用(见下文)
    • 通过将模型在多个 GPU 上分片,以网络通信为代价,从多个设备中汇集内存(模型并行),或使用其他类型的存储,如 CPU 内存或磁盘(卸载)。

    由于模型权重和不断增长的 KV 缓存必须在每次前向传递时加载,因此解码步骤涉及非常大的数据传输,正如我们将在后面的文章中看到,实际上是内存带宽受限的,即我们实际上花费更多的时间移动数据,而不是进行有用的工作,即计算。在这种情况下,延迟只能通过拥有更多内存带宽(即更好的硬件)或传输更少数据来改善。更小的模型权重和 KV 缓存释放了内存,可以容纳更多序列,因此能够提高吞吐量(和/或最大序列长度)。

    在这方面,内存占用减少策略具有三重用途,因为它们允许我们提高硬件利用率,从而提高成本效益,同时降低延迟并提高吞吐量。

    为什么我需要为输入标记付费?

    在这一点上,你应该明白为什么你需要为输入和输出标记付费。一旦输入提示被处理,即在预填充阶段结束时,我们已经消耗了 GPU 内存(用于存储每个输入标记的键和值张量)和计算(用于将提示标记通过模型)。

    让我们看看一些实际数字。假设 P 参数模型的前向传递的总 FLOPs 数量约为 2.P FLOPs/标记 [5],使用 Llama-2-7B 处理提示消耗约 0.5 MB/标记的 GPU 内存(见上文)和约 14 GFLOPs/标记的 GPU 计算。对于一个 1000 个标记的提示(略小于两页),那就是约 500MB 的内存和 14 TFLOPs 的计算,而且我们还没有生成任何内容。

    现在让我们看看所有可以减少 KV 缓存的内存占用的方法,方法是采用上面的公式,依次查看每个项:

    减少批次大小?

    在大多数情况下,我们不想减小批次大小,因为虽然它有助于减少 KV 缓存的内存占用,从而降低延迟,但它会降低硬件利用率,从而降低成本效益。在后面的文章中,我们将看到,相反,我们希望尽可能地增加批次大小。

    减少对总序列长度的依赖?

    不为序列中的所有标记存储键和值的一个原因是,我们明确选择在每次迭代时重新计算缺失的键和值,因为花费 FLOPs 比消耗 GPU 内存更划算(例如,因为我们是内存带宽受限的,在自回归阶段就是这样)。据我所知,这在实践中并不是我所知道的,所以我们不会深入研究这个方向。

    另一个角度是,我们可以不必为模型不关注或很少关注的标记存储键和值。对于经过训练只关注序列一部分的模型(例如,使用 Mistral AI 的 Mistral-7B. ,或者作为内存消耗和模型精度之间的一种折衷方案,这可能是这种情况。让我解释一下。

    像 Mistral-7B [6] 这样的模型经过训练不会关注整个序列。Mistral-7B 的注意力层实际上通过只关注最后(4096)个相邻标记来构建标记表示。这种注意力机制的变体称为滑动窗口注意力(SWA)或局部注意力。通过设计,局部注意力保证我们永远不会在 KV 缓存中为每个序列存储超过窗口大小(例如 4096)的张量对。

    另一种方法是利用注意力层在序列中将注意力分散到标记上的方式中的模式。众所周知,注意力模块不成比例地且始终如一地将更多注意力分配给序列中的一小部分标记(图 1)。相反,许多标记始终对输出的贡献很小,那么为什么要费心存储它们的键和值呢?

    通过丢弃这些标记,我们实际上将相应的注意力分数设置为零,并用更稀疏的矩阵来近似注意力矩阵。成功的近似将最大限度地减少近似误差,从而最大限度地减少对模型精度的影响(例如,使用困惑度来衡量)。

    让我们看看过去几个月出现的一些方法,这些方法可以直接应用,无需任何重新训练或微调:StreamingLLM 框架、H2O. 重击者预言机)、剪刀手和 FastGen。据我所知,目前还没有任何流行的 LLM 推理框架支持它们。

    针对使用有限长度上下文窗口训练的模型,StreamingLLM 框架 [7] 基于这样一个观察结果:初始标记会收集大量的注意力。因此,该框架通过只在缓存中保留最开始的几个位置标记(“汇聚标记”)和最后一个相邻标记(局部注意力)来构建一个滑动窗口。因此,StreamingLLM KV 缓存的长度是固定的,既有固定部分(通常为 1 到 4 个标记),也有滑动部分。

    类似的 H2O [8] 和剪刀手 [9] 方法明确地旨在通过设置一个最大缓存标记数(预算)并通过在每次达到缓存预算时丢弃标记来压缩 KV 缓存。H2O 算法一次只丢弃一个标记,而剪刀手则根据目标压缩率(例如,减少 30% 的 KV 缓存大小)丢弃尽可能多的标记。

    这两种方法都基于这样一个观察结果:在给定步骤中具有影响力的标记(“关键标记”或“重击者”)在未来步骤中仍然具有影响力(剪刀手作者称之为“重要性持久性假设”)。换句话说,我们可以确保被丢弃的低影响力标记在未来步骤中将仍然相对被忽略,因此可以安全地丢弃。

    这两种算法的一个关键方面显然是缓存驱逐策略。剪刀手只保留最新的标记和历史窗口内注意力分数最高的标记。H2O 丢弃累积注意力分数最低的标记,因此只保留在迭代过程中始终获得高注意力分数的标记。这两个作者团队都表明,他们的算法可以实现高达 80% 的 KV 缓存大小减少,而模型精度损失可以忽略不计。

    FastGen 方法 [10](不要与无关的 DeepSpeed-FastGen 混淆)仍然基于注意力模式,但采用另一种方法,即不设置缓存预算,而是设置注意力矩阵的最大近似误差,因此侧重于保持模型精度。

    FastGen 是一种两步法:首先,在预填充阶段结束时对模型的注意力层进行分析,以确定允许满足误差目标的压缩策略集。与其他方法一样,它假设所识别的注意力模式将在未来的生成步骤中保持不变。压缩策略包括:保留特殊标记、保留标点符号标记、保留最后一个相邻标记(局部注意力)等等(图 2)。如果误差目标过于严格,无法满足,FastGen 将回退到常规的 KV 缓存。然后,在每个生成步骤中将所选的压缩策略应用于 KV 缓存。

    图 2 – FastGen 论文中压缩策略集的示例:特殊标记(绿色)+ 标点符号标记(橙色)+ 局部注意力(蓝色)。被丢弃的标记用灰色表示。

    请注意,与其他方法不同,FastGen 为每个提示构建一个量身定制的压缩策略。FastGen 作者表明,对于给定的 KV 缓存压缩率,他们比 H2O 和剪刀手更好地保持模型精度。

    在任何情况下,打破对不可预测的总序列长度的依赖都是一种解脱,因为它允许为每个序列分配一个内存预算,从而大大简化内存管理。由于数据传输是延迟的主要贡献者,因此没有一个随着序列长度线性增长的 KV 缓存,尤其是在较长的序列长度下,可以带来惊人的加速。

    减少层数?

    这里没有太多可以获得的。较小的模型通常具有较少的层(表 4),因此如果较小的模型在你的用例中表现良好,只需选择它即可。

    模型参数层数头数隐藏维度
    Llama-2–7B7B32324096
    Llama-2–13B13B40404096
    Llama-2–70B70B60644096
    Llama-2–13B13B80804096

    表 4 – Llama-2 模型规格

    减少注意力头的数量?

    由于对于给定的模型架构,模型大小主要由层数和头数控制,因此减少头数可能意味着选择一个更小的模型(见表 4)。

    但是,如果我们仔细观察,我们会注意到,我们只需要减少键和值头的数量,查询头的数量不会影响 KV 缓存的大小。这正是多查询注意力(MQA)[11] 和分组查询注意力(GQA)[12] 架构背后的理念。这些多头注意力(MHA)变体的唯一动机是减少 KV 缓存的大小。

    MQA 最早于 2019 年被引入。在 MQA 中,所有查询头共享相同的单个键和值头。换句话说,所有查询头使用相同的键计算它们的注意力分数,所有头的输出使用相同的值(但不是相同的注意力分数)计算(图 3)。

    图 3 – 多头注意力(上)与多查询注意力(下)(两个注意力头)

    然而,剥离所有头对于较大的模型来说相对更激进。例如,从 64 个头减少到 1 个头,与从 32 个头减少到 1 个头相比,模型的表示能力的削减更大。GQA 通过提供一种折衷方案来解决这个问题:我们不是让所有查询头共享相同的唯一 KV 头,而是将它们分成 g 个查询头的组,同一个组的查询头共享相同的唯一 KV 头。换句话说,我们不是从 $n_{heads}$ 缩减到 1 个 KV 头,而是将 KV 头的数量从 $n_{heads}$ 缩减到 $1<g<n_{heads}$。

    从这个角度来看,MHA 和 MQA 都是 GQA 的特例(分别为 $g=1$ 和 $g=n_{heads}$)。QGA 允许在两个极端情况(MHA 和 MQA)之间更平滑地导航模型精度/KV 缓存大小(与延迟和吞吐量都相关)的折衷方案。

    考虑到这个新的参数 g,KV 缓存大小公式变为:

    $$
    KV_cache_size = b \cdot t \cdot 2 \cdot g \cdot d_{head} \cdot p_a
    $$

    在实践中,MQA/GQA 架构已被 Google Research 的 PaLM [13],TII 的 Falcon [14] 模型,Meta 的 Llama-2 [1](仅 70B. 和 Mistral AI 的 Mistral-7B [7](表 5)显著地实现。

    模型参数层数头数隐藏维度注意力类型
    PaLM540B526416384MQA
    Falcon40B40645120MQA
    Llama-2–70B70B60644096GQA
    Mistral-7B7B24324096GQA

    表 5 – 使用 MQA 或 GQA 的模型系列

    注意力头的隐藏维度?

    再次强调,如果你不准备选择另一个模型,这里没有太多可以获得的。根据模型系列的不同,头隐藏维度在不同模型大小之间可能是恒定的(例如 Llama-2,Falcon),因此选择同一个系列中较小的变体不会有帮助。

    使用更少的字节数/参数?

    量化 KV 缓存确实是一种可以大幅减少其大小的好方法。但是,像 AWQ [15] 或 GPTQ [16] 这样的仅权重量化算法从定义上来说不会有帮助。只有量化权重和“激活”(即任何不是权重的东西)的算法,例如 LLM.int8()[17] 或 SmoothQuant [18],才会产生量化的 KV 缓存。

    请注意,在权重和激活上都起作用的量化算法的意图之一是在较低的精度下执行计算密集型矩阵乘法。如果像训练期间那样是计算受限的,这会带来性能提升,但正如我们将在后面的文章中看到,推理的自回归阶段实际上是内存带宽受限的,因此能够更快地计算不会带来太多价值。

    我们实际上只对内存占用的减少感兴趣,因为它意味着更少的数据传输。从这个角度来看,像 LLM.int8() 或 SmoothQuant 这样的量化算法有点过分:在将缓存的张量移动到 GPU 内存之前对其进行量化,并在从 GPU 内存中获取相同的张量后对其进行反量化(以额外的开销为代价)应该就足够了。

    一些 LLM 推理系统已经包含了这样的 KV 缓存量化功能。例如,FlexGen [19] 将 KV 缓存和模型权重都量化并存储在 4 位数据格式中。NVIDIA TensorRT-LLM 能够将 KV 缓存量化到 8 位数据格式(INT8 或 FP8)中。流行的 vLLM 框架从 0.3.0 版本开始也支持 KV 缓存(FP8)量化。由于量化是在每次迭代时动态执行的,因此不需要校准步骤。

    高效内存管理的重要性

    到目前为止,我们隐含地假设内存没有浪费:所有保留的内存都用于存储标记,所有可用的内存都可以被保留。在实践中,简单的内存管理策略会导致大量内存被浪费(PagedAttention 论文 [20] 表明,实际的有效内存利用率可能低至 20%,即 80% 的浪费!):

    • 由于请求的总序列长度事先未知,我们可以保留能够容纳最大序列长度的连续内存块。这种分配中很大一部分肯定永远不会被使用,并且由于无法用于其他请求,因此被浪费了(内部内存碎片)。
    • 即使序列长度事先已知,由于内存是逐渐消耗的,但内存块是为请求的生命周期保留的,因此较短的请求无法使用仍然未使用的内存块。
    • 如果我们使用像束搜索这样的解码策略,为每个请求生成多个序列,那么多个候选序列实际上可以部分共享它们的 KV 缓存。如果我们不考虑这种情况,我们不可避免地会通过存储本可以共享的重复 KV 条目来浪费内存。

    这些缺点正是现在流行的 PagedAttention 算法旨在解决的问题。PagedAttention 分配固定大小且相对较小的内存块,称为块。每个块可以包含固定数量的标记,如果需要,可以跨不同的请求共享。按需分配和较小的块大小减轻了内部内存碎片,而相同大小的块消除了外部内存碎片。

    总的来说,PagedAttention 实现了 KV 缓存内存的近乎零浪费(低于 4% [21])。以前被浪费的内存现在可以用来容纳更多请求,从而提高吞吐量。PagedAttention 推出时,吞吐量提高的数字与当时内存浪费的程度一样惊人。

    PagedAttention 最初由 vLLM 推理系统实现,但现在所有主要的推理框架都支持它(例如 HuggingFace TGI、NVIDIA TensorRT-LLM、LMDeploy TurboMind 等)。

    PagedAttention 未涵盖的另一个可能的优化是在请求之间重用键值缓存。这将适用于提示共享公共前缀的情况,这种情况在像聊天和代理这样的多轮用例中很常见,或者在使用提示模板时(图 4)。

    图 4 – SGLang 论文中 KV 缓存共享示例(多轮聊天),总共四个生成请求。蓝色框表示可共享的提示部分。

    能够在请求之间重用 KV 缓存将能够显著降低延迟(尤其是第一个标记的延迟)和吞吐量(通过大大减少具有共享前缀的并发请求的内存占用)。

    LMSYS SGLang 论文 [22] 中介绍的 RadixAttention 技术实现了这种 KV 缓存重用。

    RadixAttention 算法不是在完成生成请求后丢弃 KV 缓存,而是将其保留在 GPU 内存中,并向一个专门的数据结构(基数树)添加一个新条目,该结构将标记序列映射到它们的 KV 缓存张量。当一个新的请求进来时,调度程序使用基数树进行前缀匹配。如果有缓存命中,调度程序将重用缓存的 KV 张量来满足请求。

    由于 GPU 内存有限,缓存的 KV 张量不能永远保留。因此,RadixAttention 算法包含一个驱逐策略(例如,最近最少使用 (LRU) 驱逐策略)。最佳缓存重用可能与先到先得的调度程序不兼容。因此,RadixAttention 带有一个修改后的调度程序,该调度程序优先考虑与缓存的前缀匹配的请求(缓存感知调度)。

    注意: PagedAttention 和 RadixAttention 的命名有点误导,因为与人们可能认为的相反,它们不是模型注意力层的优化(如 FlashAttention),而是在模型服务器级别运行(它们帮助服务应用程序更好地管理主机上的 KV 缓存)。

    如果 GPU 内存不足,为什么不“只”使用多个 GPU?或者卸载到 CPU 内存甚至磁盘?

    这些是两种不同的但有效的方案。

    首先关于卸载到更丰富但更慢的存储器(CPU 内存和磁盘)。并非所有推理框架都支持此功能,让我们列举 HuggingFace Accelerate、DeepSpeed-Inference 和更先进的 FlexGen。由于它涉及使用更慢的存储器,因此卸载会以较大的延迟损失为代价,因此对于对延迟敏感的用例来说,显然不应优先考虑此选项。卸载系统通常用于面向吞吐量的用例,例如离线批处理。

    关于使用多个 GPU(对于较大的模型来说不可避免),将模型在多个设备上分片可以释放内存压力,因为可以从聚合的内存容量和内存带宽中获益。

    如果选择管道并行 [23],模型和 KV 缓存都将在层维度上分片。如果选择张量并行 [24](更常见于推理),KV 缓存将在头维度上分片。请注意,MQA 在这种设置下效率很低:由于我们无法跨多个设备分片单个头,因此 KV 缓存必须在所有设备上复制,从而失去了 MQA 的优势。对于实现 MQA 的模型,另一种选择是跨批次大小维度分片 KV 缓存 [25]。

    在任何情况下,以上所有情况都假设单个主机,我们仍然受限于我们可以得到的最大多 GPU 实例的存储容量。据我所知,目前还没有推理框架支持多主机模型并行。如果我们能够将模型和 KV 缓存都分片到多个主机上,那么我们可以处理的可用内存量和最大序列长度将实际上变得无限。这是 Infinite-LLM 论文 [26] 旨在通过引入一种新的分布式注意力算法 (DistAttention) 和调整 Ray 框架来构建多主机分布式 KV 缓存管理和调度系统 (DistKV-LLM) 来解决的问题。

    总结

    在这篇文章中,我们学习了选择 KV 缓存会带来额外的挑战。多头注意力 (MHA) 模型的 KV 缓存确实会消耗大量 GPU 内存,大约为 1MB/标记,并且很容易超过模型权重的内存占用。

    鉴于 GPU 内存的有限性,KV 缓存内存压力导致了许多不同方向的举措:新的注意力架构(MQA、GQA、SWA)、缓存压缩策略(H2O. 剪刀手、FastGen)、高效内存管理(PagedAttention、RadixAttention)、量化和存储容量扩展(卸载系统、单主机和多主机模型并行)。

    正如我们将在后面的文章中看到,减少 KV 缓存的大小至关重要,不仅因为 GPU 内存有限,而且因为数据移动量实际上是每个自回归步骤的延迟的主要贡献者,因此也是整个生成过程的延迟的主要贡献者。

    在下一篇文章中,我们将看看可能影响模型延迟和吞吐量的不同类型的瓶颈。我们下次再见!

    参考文献:

    [1]: Llama 2: Open Foundation and Fine-Tuned Chat Models (Touvron 等人,2023)
    [2]: OPT: Open Pre-trained Transformer Language Models (Zhang 等人,2022)
    [3]: MPT-7B. 2023 年 5 月)和 MPT-30B(2023 年 6 月)的发布博客文章
    [4]: BLOOM: A 176B-Parameter Open-Access Multilingual Language Model (BigScience,2023)
    [5]: Scaling Laws for Neural Language Models (Kaplan 等人,2020)
    [6]: Mistral 7B (Jiang 等人,2023)
    [7]: Efficient Streaming Language Models with Attention Sinks (Xiao 等人,2023) + GitHub 仓库
    [8]: H_2O. Heavy-Hitter Oracle for Efficient Generative Inference of Large Language Models (Zhang 等人,2023) + GitHub 仓库
    [9]: Scissorhands: Exploiting the Persistence of Importance Hypothesis for LLM KV Cache Compression at Test Time (Liu 等人,2023)
    [10]: Model Tells You What to Discard: Adaptive KV Cache Compression for LLMs (Ge 等人,2023)
    [11]: Fast Transformer Decoding: One Write-Head is All You Need (Shazeer,2019)
    [12]: GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints (Ainslie 等人,2023)
    [13]: PaLM: Scaling Language Modeling with Pathways (Chowdhery 等人,2022)
    [14]: The Falcon Series of Open Language Models (Almazrouei 等人,2023)
    [15]: AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration (Lin 等人,2023) + GitHub 仓库
    [16]: GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers (Frantar 等人,2022) + GitHub 仓库
    [17]: LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale (Dettmers 等人,2022) + GitHub 仓库
    [18]: SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models (Xiao 等人,2022) + GitHub 仓库
    [19]: FlexGen: High-Throughput Generative Inference of Large Language Models with a Single GPU (Sheng 等人,2023) + GitHub 仓库
    [20]: Efficient Memory Management for Large Language Model Serving with PagedAttention (Kwon 等人,2023) + GitHub 仓库
    [21]: vLLM: Easy, Fast, and Cheap LLM Serving with PagedAttention (Kwon 等人,2023)
    [22]: Efficiently Programming Large Language Models using SGLang (Zheng 等人,2023) + 博客文章
    [23]: GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism (Huang 等人,2018)
    [24]: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM (Narayanan 等人,2021)
    [25]: Efficiently Scaling Transformer Inference (Pope 等人,2022)
    [26]: Infinite-LLM: Efficient LLM Service for Long Context with DistAttention and Distributed KVCache (Lin 等人,2024)

  • 告别矩阵乘法:探索轻量级语言模型的新纪元

    大型语言模型(LLM)在自然语言处理领域取得了巨大成功,但其高昂的计算成本和庞大的内存需求也成为了限制其应用的瓶颈。矩阵乘法(MatMul)是LLM中最主要的计算操作,其占用了大部分的计算时间和内存资源。为了解决这一问题,来自加州大学圣克鲁兹分校的研究人员提出了一种全新的,可扩展的无矩阵乘法语言模型(MatMul-free LM),该模型在保持强大性能的同时,彻底消除了所有矩阵乘法操作。

    为什么矩阵乘法如此重要?

    矩阵乘法在神经网络中无处不在,从密集层到卷积层,再到自注意力机制,都离不开矩阵乘法。这主要是因为现代图形处理单元(GPU)对矩阵乘法操作进行了高度优化。通过利用CUDA和cuBLAS等线性代数库,矩阵乘法可以被高效地并行化和加速。这使得AlexNet在2012年ImageNet竞赛中取得了胜利,并推动了深度学习的快速发展。

    然而,矩阵乘法操作也带来了巨大的计算成本和内存消耗。在训练和推理阶段,矩阵乘法通常占用了绝大部分的执行时间和内存访问。因此,研究人员一直在探索用更简单的操作来替代矩阵乘法。

    现有方法的局限性

    目前,替代矩阵乘法的方法主要有两种:

    1. 用基本运算替代矩阵乘法: 例如AdderNet用带符号的加法来替代卷积神经网络中的乘法。但AdderNet主要针对计算机视觉任务,在语言建模方面效果不佳。
    2. 二值化或三值化: 将矩阵中的元素量化为二进制或三进制,从而将矩阵乘法简化为简单的加减运算。这种方法可以应用于激活值或权重。例如,脉冲神经网络(SNN)使用二值化的激活值,而二值化神经网络(BNN)和三值化神经网络(TNN)使用量化的权重。

    近年来,BitNet等语言模型证明了量化方法的可扩展性,将所有密集层权重替换为二进制或三进制值,支持高达30亿个参数。然而,BitNet仍然保留了自注意力机制,而自注意力机制仍然依赖于昂贵的矩阵乘法。

    MatMul-free LM的创新之处

    为了彻底消除LLM中的矩阵乘法,研究人员提出了MatMul-free LM,该模型利用了密集层中的加法运算和自注意力机制中的逐元素哈达玛积。

    1. 三值化权重: 类似于BNN,MatMul-free LM将密集层中的权重限制为{-1, 0, +1},从而将矩阵乘法转换为简单的加减运算。

    2. 无矩阵乘法线性门控循环单元(MLGRU): 为了消除自注意力机制中的矩阵乘法,研究人员对门控循环单元(GRU)进行了优化,使其仅依赖于逐元素乘法。

    3. 无矩阵乘法门控线性单元(GLU): MatMul-free LM使用GLU作为通道混合器,并将其中的密集层替换为三值化权重,从而消除了矩阵乘法。

    MatMul-free LM的优势

    MatMul-free LM具有以下优势:

    • 计算效率更高: 消除了矩阵乘法操作,大幅减少了计算时间。
    • 内存需求更低: 三值化权重减少了模型的内存占用。
    • 硬件友好: 更适合在FPGA等专用硬件上实现。

    实验结果

    研究人员对MatMul-free LM进行了实验,并将其与Transformer++模型进行了比较,结果表明:

    • MatMul-free LM在性能上与Transformer++相当,甚至在某些情况下表现更佳。
    • MatMul-free LM在训练和推理阶段的效率都更高,内存占用和延迟都更低。
    • MatMul-free LM在FPGA上的实现也取得了成功,其效率接近人脑。

    未来展望

    MatMul-free LM的出现,为构建更高效、更节能的LLM开辟了新的道路。随着LLM在各种平台上的应用不断扩展,MatMul-free LM将成为构建高效、可扩展的LLM的重要方向。

    参考文献:

    [1] A. Krizhevsky, I. Sutskever, and G. E. Hinton. Imagenet classification with deep convolutional neural networks. In Advances in neural information processing systems, pp. 1097–1105, 2012.

    [2] Y. LeCun, Y. Bengio, and G. Hinton. Deep learning. Nature, 521(7553):436–444, 2015.

    [3] S. Han, H. Mao, and W. J. Dally. Deep compression: Compressing deep neural networks with pruning, trained quantization and huffman coding. In International Conference on Learning Representations, 2016.

    [4] S. B. Furber. Neuromorphic engineering. The MIT Press, 2016.

    [5] G. Indiveri, B. Linares-Barranco, R. Legenstein, D. Chicca, G. Indiveri, B. Linares-Barranco, R. Legenstein, D. Chicca, and A. Hamilton. Neuromorphic silicon. Springer, 2011.

    [6] T. Masquelier, S. Thornton, S. B. Furber, and J. V. Pulvermüller. A spiking neural network model of word recognition in the human brain. PLoS computational biology, 10(12):e1003974, 2014.

    [7] I. Hubara, M. Courbariaux, D. Soudry, R. El-Yaniv, and Y. Bengio. Binarized neural networks. In Advances in neural information processing systems, pp. 4107–4115, 2016.

    [8] M. Courbariaux, Y. Bengio, and J.-P. Salinas. Binaryconnect: Training deep neural networks with binary weights during backpropagation. In Advances in neural information processing systems, pp. 4107–4115, 2015.

    [9] R. Zhu, Y. Zhang, E. Sifferman, T. Sheaves, Y. Wang, D. Richmond, P. Zhou, and J. K. Eshraghian. Scalable MatMul-free Language Modeling. arXiv preprint arXiv:2406.02528, 2024.

    [10] L. Pei, S. Li, S. Zhang, J. Li, and S. Liu. BitNet: A Billion-Parameter Binary and Ternary Neural Network for Language Modeling. arXiv preprint arXiv:2302.03633, 2023.

    [11] L. Pei, S. Li, S. Zhang, J. Li, and S. Liu. BitNet: A Billion-Parameter Binary and Ternary Neural Network for Language Modeling. arXiv preprint arXiv:2302.03633, 2023.

    [12] T. Chen, Z. Du, N. Sun, J. Wang, C. Wu, Y. Chen, H. Li, Y. Tang, Y. Wang, and X. Lin. Training deep neural networks with 8-bit floating point numbers. In Advances in Neural Information Processing Systems, pp. 6083–6092, 2018.

    [13] K. Cho, B. van Merriënboer, C. Gulcehre, D. Bahdanau, F. Bougares, H. Schwenk, and Y. Bengio. Learning phrase representations using RNN encoder-decoder for statistical machine translation. In Proceedings of the 2014 Conference on Empirical Methods in Natural Language Processing (EMNLP), pp. 1724–1734, 2014.

    [14] D. M. K. Pramanik, S. Bhattacharyya, and P. Das. Ternary BERT: Low-Precision BERT for Resource-Constrained Devices. arXiv preprint arXiv:2004.06633, 2020.

    [15] Y. Sun, Y. Zhang, Z. Liu, Y. Liu, and J. Tang. Quantized BERT: Efficient BERT for Resource-Constrained Devices. arXiv preprint arXiv:1910.04432, 2019.

    [16] Y. Wang, Y. Zhang, Y. Sun, J. Tang, and Z. Liu. Incremental Network Quantization: Towards Lossless CNNs with Low-Precision Weights. arXiv preprint arXiv:2002.08150, 2020.

    [17] J. Zhou, Z. Sun, A. Zou, Q. Liu, and Y. Gong. Training Low-Precision Deep Neural Networks via Quantization-Aware Training. arXiv preprint arXiv:1905.04893, 2019.

    [18] C. Lee, S. Lee, H. Kim, and J. Shin. Spikformer: Spiking Neural Networks for Efficient Transformer. arXiv preprint arXiv:2103.13518, 2021.

    [19] C. Lee, S. Lee, H. Kim, and J. Shin. Spikformer: Spiking Neural Networks for Efficient Transformer. arXiv preprint arXiv:2103.13518, 2021.

    [20] A. S. M. A. Saleh, A. A. M. Al-Jumaily, and A. Al-Ani. Spike-Driven Transformer for Image Classification. arXiv preprint arXiv:2203.08669, 2022.

    [21] S. M. A. Saleh, A. A. M. Al-Jumaily, and A. Al-Ani. Spike-Driven Transformer for Image Classification. arXiv preprint arXiv:2203.08669, 2022.

    [22] X. Li, L. Huang, J. Li, and Y. Chen. Spiking-BERT: A Spiking Neural Network for Sentiment Analysis. arXiv preprint arXiv:2106.07442, 2021.

    [23] M. A. Saleh, A. A. M. Al-Jumaily, and A. Al-Ani. SpikeBERT: A Spiking Neural Network for Sentiment Analysis. arXiv preprint arXiv:2110.03458, 2021.

    [24] A. Parmar, A. Vaswani, J. Uszkoreit, L. Kaiser, N. Shazeer, A. Ku, and D. Tran. Image transformer. In International Conference on Machine Learning, pp. 3887–3896, 2018.

    [25] A. Vaswani, N. Shazeer, N. Parmar, J. Uszkoreit, L. Jones, A. N. Gomez, Ł. Kaiser, and I. Polosukhin. Attention is all you need. In Advances in neural information processing systems, pp. 5998–6008, 2017.

    [26] S. Liu, Z. Chen, Y. Li, Z. Liu, and W. Zhang. Mamba: A Low-Resource and Efficient Transformer. arXiv preprint arXiv:2106.02256, 2021.

    [27] D. Bahdanau, K. Cho, and Y. Bengio. Neural machine translation by jointly learning to align and translate. In International Conference on Learning Representations, 2015.

    [28] P. Ramachandran, B. Zoph, and Q. Le. Swish: A self-gated activation function. arXiv preprint arXiv:1710.05941, 2017.

    [29] A. Courbariaux, R. Bengio, and J.-P. Salinas. Binaryconnect: Training deep neural networks with binary weights during backpropagation. In Proceedings of the 2015 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP), pp. 4107–4115, 2015.

    [30] I. Hubara, M. Courbariaux, D. Soudry, R. El-Yaniv, and Y. Bengio. Binarized neural networks. In Advances in Neural Information Processing Systems, pp. 4107–4115, 2016.

    [31] S. Hochreiter and J. Schmidhuber. Long short-term memory. Neural computation, 9(8):1735–1780, 1997.

    [32] S. Z. Li, Y. H. Zou, T. Y. Liu, and B. Zhang. Linear recurrent unit. In Proceedings of the 2018 Conference on Empirical Methods in Natural Language Processing, pp. 1780–1789, 2018.

    [33] A. Radford, J. Wu, R. Sutskever, and I. Sutskever. Learning to generate text with recurrent neural networks. arXiv preprint arXiv:1701.07875, 2017.

    [34] B. Wang. RWKV: A New Type of Recurrent Neural Network. arXiv preprint arXiv:2210.00078, 2022.

    [35] L. Pei, S. Li, S. Zhang, J. Li, and S. Liu. BitNet: A Billion-Parameter Binary and Ternary Neural Network for Language Modeling. arXiv preprint arXiv:2302.03633, 2023.

    [36] A. Touvron, T. Bernard, J. Caillard, G. Lample, and E. Lemoine. Llama: Open and Efficient Large Language Models. arXiv preprint arXiv:2302.09492, 2023.

    [37] A. Touvron, T. Bernard, J. Caillard, G. Lample, and E. Lemoine. Llama: Open and Efficient Large Language Models. arXiv preprint arXiv:2302.09492, 2023.

    [38] A. Touvron, T. Bernard, J. Caillard, G. Lample, and E. Lemoine. Llama: Open and Efficient Large Language Models. arXiv preprint arXiv:2302.09492, 2023.

    [39] Mistral AI. Mistral AI: A New Open Source LLM. https://mistral.ai, 2023.

    [40] Y. Bengio, N. L. C. da Silva, J. S. S. Souza, and P. Vincent. Neural networks for speech recognition. The Journal of the Acoustical Society of America, 131(3):1852–1865, 2012.

    [41] M. Courbariaux, Y. Bengio, and J.-P. Salinas. Binaryconnect: Training deep neural networks with binary weights during backpropagation. In Proceedings of the 2015 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP), pp. 4107–4115, 2015.

  • JStarCraft RNS: 一款全面的Java推荐与搜索引擎

    在信息爆炸的时代,如何从海量信息中找到我们真正需要的内容,成为了一个重要问题。JStarCraft RNS 应运而生,它是一款专注于解决推荐领域与搜索领域的两个核心问题:排序预测(Ranking)和评分预测(Rating)的Java推荐与搜索引擎。它为相关领域的研发人员提供完整的通用设计与参考实现,涵盖了70多种排序预测与评分预测算法,是目前最快速、最全面的Java推荐与搜索引擎之一。

    JStarCraft RNS 的核心优势

    JStarCraft RNS 拥有以下几个核心优势:

    • 跨平台: 支持多种操作系统,方便用户在不同环境下使用。
    • 串行与并行计算: 灵活适应不同场景,提高计算效率。
    • CPU与GPU硬件加速: 充分利用硬件资源,提升性能。
    • 模型保存与装载: 方便用户保存和复用训练好的模型。
    • 丰富的推荐与搜索算法: 提供了多种排序和评分算法,满足不同需求。
    • 丰富的脚本支持: 支持多种脚本语言,例如 Groovy、JS、Lua、MVEL、Python 和 Ruby,方便用户定制化开发。
    • 丰富的评估指标: 提供了多种排序和评分指标,帮助用户评估模型性能。

    JStarCraft RNS 的安装与使用

    JStarCraft RNS 要求使用者具备以下环境:

    • JDK 8 或者以上
    • Maven 3

    安装步骤:

    1. 安装 JStarCraft-Core 框架:
       git clone https://github.com/HongZhaoHua/jstarcraft-core.git
       mvn install -Dmaven.test.skip=true
    1. 安装 JStarCraft-AI 框架:
       git clone https://github.com/HongZhaoHua/jstarcraft-ai.git
       mvn install -Dmaven.test.skip=true
    1. 安装 JStarCraft-RNS 引擎:
       git clone https://github.com/HongZhaoHua/jstarcraft-rns.git
       mvn install -Dmaven.test.skip=true

    使用步骤:

    1. 设置依赖: 在您的项目中添加 JStarCraft RNS 的 Maven 或 Gradle 依赖。
    2. 构建配置器: 使用 Configurator 类加载配置文件,配置模型训练和评估参数。
    3. 训练与评估模型: 使用 RankingTaskRatingTask 类训练和评估模型,并获取模型评估指标。
    4. 获取模型: 使用 task.getModel() 方法获取训练好的模型。

    JStarCraft RNS 的架构与概念

    JStarCraft RNS 的核心概念包括:

    • 信息检索: 由于信息过载,信息检索的任务就是联系用户和信息,帮助用户找到对自己有价值的信息,并帮助信息暴露给对它感兴趣的用户。
    • 搜索与推荐: 搜索是主动明确的,推荐是被动模糊的。两者是互补的工具。
    • 排序预测(Ranking)与评分预测(Rating): Ranking 算法基于隐式反馈数据,关注用户的排序偏好;Rating 算法基于显示反馈数据,关注用户的评分满意度。

    JStarCraft RNS 的示例

    JStarCraft RNS 支持多种脚本语言,例如 BeanShell、Groovy、JS、Kotlin、Lua、Python 和 Ruby。用户可以使用这些脚本语言定制化开发模型训练和评估流程。

    例如,以下代码展示了如何使用 BeanShell 脚本训练和评估模型:

    // 构建配置
    keyValues = new Properties();
    keyValues.load(loader.getResourceAsStream("data.properties"));
    keyValues.load(loader.getResourceAsStream("model/benchmark/randomguess-test.properties"));
    configurator = new Configurator(keyValues);
    
    // 此对象会返回给Java程序
    _data = new HashMap();
    
    // 构建排序任务
    task = new RankingTask(RandomGuessModel.class, configurator);
    // 训练与评估模型并获取排序指标
    measures = task.execute();
    _data.put("precision", measures.get(PrecisionEvaluator.class));
    _data.put("recall", measures.get(RecallEvaluator.class));
    
    // 构建评分任务
    task = new RatingTask(RandomGuessModel.class, configurator);
    // 训练与评估模型并获取评分指标
    measures = task.execute();
    _data.put("mae", measures.get(MAEEvaluator.class));
    _data.put("mse", measures.get(MSEEvaluator.class));
    
    _data;

    JStarCraft RNS 的对比

    JStarCraft RNS 提供了丰富的排序和评分算法,用户可以根据自己的需求选择合适的算法。它还提供了一系列评估指标,帮助用户评估模型性能。

    例如,以下表格展示了 JStarCraft RNS 中提供的部分排序算法和评分算法的对比:

    算法名称问题说明/论文
    RandomGuessRanking Rating随机猜测
    MostPopularRanking最受欢迎
    ConstantGuessRating常量猜测
    GlobalAverageRating全局平均
    ItemAverageRating物品平均
    ItemClusterRating物品聚类
    UserAverageRating用户平均
    UserClusterRating用户聚类
    AoBPRRankingImproving pairwise learning for item recommendation from implicit feedback
    BPRRankingBPR: Bayesian Personalized Ranking from Implicit Feedback
    CLiMFRankingCLiMF: learning to maximize reciprocal rank with collaborative less-is-more filtering
    EALSRankingCollaborative filtering for implicit feedback dataset
    FISMRankingFISM: Factored Item Similarity Models for Top-N Recommender Systems
    GBPRRankingGBPR: Group Preference Based Bayesian Personalized Ranking for One-Class Collaborative Filtering
    HMMForCFRankingA Hidden Markov Model Purpose: A class for the model, including parameters
    ItemBigramRankingTopic Modeling: Beyond Bag-of-Words
    LambdaFMRankingLambdaFM: Learning Optimal Ranking with Factorization Machines Using Lambda Surrogates
    LDARankingLatent Dirichlet Allocation for implicit feedback
    ListwiseMFRankingList-wise learning to rank with matrix factorization for collaborative filtering
    PLSARankingLatent semantic models for collaborative filtering
    RankALSRankingAlternating Least Squares for Personalized Ranking
    RankSGDRankingCollaborative Filtering Ensemble for Ranking
    SLIMRankingSLIM: Sparse Linear Methods for Top-N Recommender Systems
    WBPRRankingBayesian Personalized Ranking for Non-Uniformly Sampled Items
    WRMFRankingCollaborative filtering for implicit feedback datasets
    Rank-GeoFMRankingRank-GeoFM: A ranking based geographical factorization method for point of interest recommendation
    SBPRRankingLeveraging Social Connections to Improve Personalized Ranking for Collaborative Filtering
    AssociationRuleRankingA Recommendation Algorithm Using Multi-Level Association Rules
    PRankDRankingPersonalised ranking with diversity
    AsymmetricSVD++RatingFactorization Meets the Neighborhood: a Multifaceted Collaborative Filtering Model
    AutoRecRatingAutoRec: Autoencoders Meet Collaborative Filtering
    BPMFRatingBayesian Probabilistic Matrix Factorization using Markov Chain Monte Carlo
    CCDRatingLarge-Scale Parallel Collaborative Filtering for the Netflix Prize
    FFMRatingField Aware Factorization Machines for CTR Prediction
    GPLSARatingCollaborative Filtering via Gaussian Probabilistic Latent Semantic Analysis
    IRRGRatingExploiting Implicit Item Relationships for Recommender Systems
    MFALSRatingLarge-Scale Parallel Collaborative Filtering for the Netflix Prize
    NMFRatingAlgorithms for Non-negative Matrix Factorization
    PMFRatingPMF: Probabilistic Matrix Factorization
    RBMRatingRestricted Boltzman Machines for Collaborative Filtering
    RF-RecRatingRF-Rec: Fast and Accurate Computation of Recommendations based on Rating Frequencies
    SVD++RatingFactorization Meets the Neighborhood: a Multifaceted Collaborative Filtering Model
    URPRatingUser Rating Profile: a LDA model for rating prediction
    RSTERatingLearning to Recommend with Social Trust Ensemble
    SocialMFRatingA matrix factorization technique with trust propagation for recommendation in social networks
    SoRecRatingSoRec: Social recommendation using probabilistic matrix factorization
    SoRegRatingRecommender systems with social regularization
    TimeSVD++RatingCollaborative Filtering with Temporal Dynamics
    TrustMFRatingSocial Collaborative Filtering by Trust
    TrustSVDRatingTrustSVD: Collaborative Filtering with Both the Explicit and Implicit Influence of User Trust and of Item Ratings
    PersonalityDiagnosisRatingA brief introduction to Personality Diagnosis
    SlopeOneRatingSlope One Predictors for Online Rating-Based Collaborative Filtering
    EFMRanking RatingExplicit factor models for explainable recommendation based on phrase-level sentiment analysis
    TF-IDFRanking词频-逆文档频率
    HFTRatingHidden factors and hidden topics: understanding rating dimensions with review text
    TopicMFRatingTopicMF: Simultaneously Exploiting Ratings and Reviews for Recommendation

    总结

    JStarCraft RNS 是一款功能强大、易于使用、性能优异的 Java 推荐与搜索引擎。它为相关领域的研发人员提供了全面的通用设计与参考实现,是构建推荐与搜索系统不可或缺的工具。

    参考文献:

  • 基于图形的混合推荐系统:GHRS

    在过去十年中,推荐系统的研究和应用变得越来越重要。这些系统可以帮助用户找到他们可能感兴趣的项目,从而提高公司的销售额和用户体验。然而,传统的推荐系统存在一些问题,如冷启动问题和基于内容和协作 filtering 的限制。

    为了解决这些问题,我们提出了一个基于图形的混合推荐系统(GHRS),它结合了自动编码器和图形模型的优势。GHRS 使用自动编码器来提取用户和项目的特征,然后使用图形模型来模拟用户之间和项目之间的关系。

    推荐系统的类型

    推荐系统可以分为基于内容、协作 filtering 和混合类型。基于内容的推荐系统使用项目的特征来推荐项目,而协作 filtering 则使用用户的评分记录来推荐项目。混合类型的推荐系统结合了基于内容和协作 filtering 的优势。

    GHRS 的架构

    GHRS 的架构主要包括三个部分:自动编码器、图形模型和推荐模块。自动编码器用于提取用户和项目的特征,图形模型用于模拟用户之间和项目之间的关系,推荐模块则根据用户的特征和项目的特征来推荐项目。

    GHRS 的优点

    GHRS 相比于传统的推荐系统具有以下几个优点:

    • 能够解决冷启动问题:GHRS 使用自动编码器来提取用户和项目的特征,从而解决了冷启动问题。
    • 能够提高推荐准确性:GHRS 结合了自动编码器和图形模型的优势,能够更好地捕捉用户和项目之间的关系,从而提高推荐的准确性。
    • 能够适应不同的应用场景:GHRS 可以应用于不同的应用场景,如电影推荐、音乐推荐、商品推荐等。

    结论

    GHRS 是一个基于图形的混合推荐系统,它结合了自动编码器和图形模型的优势,能够解决冷启动问题和提高推荐准确性。GHRS 可以应用于不同的应用场景,具有广泛的应用前景。

  • 变形金刚竟是状态空间模型?揭秘高效序列模型的奥秘

    近年来,变形金刚(Transformers)凭借其强大的能力,在自然语言处理领域取得了巨大成功,成为深度学习的主流架构之一。然而,近年来,结构化状态空间模型(SSMs),例如Mamba,在小型到中型模型上展现出与变形金刚相媲美甚至超越的性能。

    本文将深入探讨变形金刚和状态空间模型之间的深层联系,揭示它们之间的结构化状态空间对偶性(SSD),并以此为基础,提出了一种名为Mamba-2的新型架构,它不仅在语言建模方面与变形金刚竞争,而且训练速度更快,效率更高。

    变形金刚和 SSM 的深层联系

    变形金刚的核心是注意力机制,它通过对序列中每个元素之间的关系进行评分,让每个元素能够“关注”其他元素。然而,注意力机制的计算复杂度较高,特别是当序列长度较长时,训练和推理的效率会急剧下降。

    另一方面,状态空间模型则通过递归的方式来处理序列,它们将输入序列映射到一个隐含的状态空间,并通过对状态空间进行操作来生成输出。与注意力机制相比,状态空间模型的计算复杂度更低,特别是在处理长序列时。

    那么,变形金刚和 SSM 之间到底有什么联系呢?本文揭示了它们之间的结构化状态空间对偶性(SSD),它通过结构化矩阵的概念,将两者联系起来。

    结构化矩阵:高效计算的桥梁

    结构化矩阵是指那些具有亚二次参数乘法复杂度的矩阵。它们可以被压缩表示,并通过高效的算法进行操作。

    本文证明了状态空间模型等价于一类被称为半可分离矩阵(Semiseparable Matrices)的结构化矩阵。这种等价性揭示了状态空间模型的全新特性,并为其设计高效算法提供了新的思路。

    结构化掩码注意力:线性注意力的扩展

    线性注意力(Linear Attention)是注意力机制的一种变体,它通过将注意力矩阵分解为两个矩阵的乘积,并利用矩阵乘法的结合律,将注意力机制的计算复杂度从二次降到线性。

    本文通过张量收缩的概念,对线性注意力进行了重新推导,并将其扩展到更一般的结构化掩码注意力(SMA)。SMA 允许使用任何具有亚二次乘法复杂度的结构化矩阵作为掩码,从而进一步提高了注意力的效率。

    结构化状态空间对偶性:将 SSM 和 SMA 联系起来

    本文的核心结论是,状态空间模型结构化掩码注意力之间存在着一种结构化状态空间对偶性(SSD)

    具体而言,当状态空间模型中的状态转移矩阵具有标量乘以单位矩阵的结构,而结构化掩码注意力中的掩码矩阵为1-半可分离矩阵时,它们就具有相同的线性形式和二次形式。

    这种对偶性意味着,我们可以将设计用于注意力的算法和优化技术应用到状态空间模型中,反之亦然。

    Mamba-2 架构:将 SSD 应用于语言建模

    基于 SSD 框架,本文提出了一种名为Mamba-2的新型架构,它将 SSD 层作为核心层,并结合了其他优化技术,例如并行参数投影额外归一化多头模式等。

    Mamba-2 在语言建模方面展现出与变形金刚相媲美的性能,同时训练速度更快,效率更高。

    总结

    本文揭示了变形金刚和状态空间模型之间的深层联系,并提出了一个名为 SSD 的理论框架,它通过结构化矩阵的概念,将两者联系起来。SSD 框架为设计更高效的序列模型提供了新的思路,并为 Mamba-2 架构的提出提供了理论基础。

    参考文献

    [1] Dao, Tri, and Albert Gu. “Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality.” arXiv preprint arXiv:2305.11024 (2023).


    Q: 这篇论文试图解决什么问题?

    A: 这篇论文的主要目标是建立结构化状态空间模型(SSMs)和注意力机制(Transformers中的一个关键组件)之间的理论联系。具体来说,它试图解决以下问题:

    1. 模型效率问题:Transformers在处理长序列时面临效率问题,因为它们的注意力机制在序列长度上呈二次方增长。而SSMs提供了一种线性复杂度的序列处理方式,但它们的发展似乎与Transformers的优化和硬件适应性改进相脱节。
    2. 理论联系缺失:SSMs和Transformers的优化工作在社区中相对独立,缺乏理论上的联系和相互借鉴,这使得SSMs相比Transformers更难以理解和实验。
    3. 算法和系统优化转移:论文希望将为Transformers开发的算法和系统优化转移到SSMs中,以构建性能更好、序列长度扩展性更高的基础模型。
    4. 设计新架构:通过理论联系,论文提出了一种新的架构(Mamba-2),其核心层是对Mamba的SSM的选择性模型的改进,旨在提高计算速度,同时在语言建模方面与Transformers保持竞争力。
    5. 系统优化:论文还探讨了如何将SSMs与Transformers相连接,从而利用为Transformers开发的系统优化工作,例如张量并行性(TP)、序列并行性等。

    总的来说,论文试图通过建立SSMs和注意力机制之间的理论桥梁,促进两者的相互理解和优化,推动更高效、更可扩展的深度学习模型的发展。

    Q: 有哪些相关研究?

    A: 论文中提到了多个与结构化状态空间模型(SSMs)、注意力机制以及相关算法和系统优化相关的研究。以下是一些主要的相关研究:

    1. Transformers:特别是解码器仅模型(如GPT和Llama),这些模型是深度学习在语言建模方面成功的主要驱动力。
    2. Mamba:一种结构化SSM,它在小到中等规模上展示了与Transformers相匹配或更好的性能。
    3. Linear Attention (LA) 框架:由Katharopoulos等人提出,它展示了自回归注意力和线性RNNs之间的联系。
    4. FlashAttention-2:由Dao提出,这是一种优化的Softmax注意力实现。
    5. Megatron:Shoeybi等人提出的一种模型并行性技术,用于训练大型Transformer模型。
    6. S4:Gu等人提出的结构化SSM,用于长序列建模。
    7. GateLoop:Katsch提出的一个模型,它使用输入依赖的衰减因子。
    8. Gated Linear Attention (GLA):Yang等人提出的一个模型,它在线性注意力中引入了数据依赖的门控机制。
    9. HGRN:Qin等人提出的一个RNN模型,它结合了输入依赖的门控和状态扩展。
    10. Griffin:De等人提出的一个模型,它结合了门控线性递归和局部注意力。
    11. xLSTM:Beck等人提出的一个改进的长短期记忆网络(LSTM)模型。
    12. RWKV:Peng等人提出的一个基于不同线性注意力近似的RNN模型。

    这些研究为论文提出的结构化状态空间对偶(SSD)框架提供了理论基础和技术支持。论文通过与这些相关工作的比较和联系,展示了SSD框架的优势和潜在的应用前景。

    Q: 论文如何解决这个问题?

    A: 论文通过提出一个名为结构化状态空间对偶(Structured State Space Duality, SSD)的框架来解决上述问题。SSD框架旨在建立结构化状态空间模型(SSMs)和注意力机制之间的理论联系,并开发出新的算法和架构设计。具体来说,论文采取了以下几个步骤:

    1. 理论联系:展示了SSMs和注意力机制实际上是密切相关的,通过研究一类被称为结构化半可分矩阵的特定矩阵类来建立联系。
    2. 状态空间模型(SSM)与结构化矩阵的等价性:证明了SSMs可以表示为结构化矩阵,特别是半可分矩阵,这揭示了SSMs的新属性和算法。
    3. 线性注意力理论的改进:扩展了线性注意力的理论,提供了通过张量收缩语言的递归形式的新证明,并将线性注意力推广到新的结构化掩蔽注意力(Structured Masked Attention, SMA)。
    4. SSMs与SMA的连接:展示了SSMs和SMA之间存在大量的交集,并且这些模型是彼此的对偶,具有SSM类的线性和注意力类的二次形式。
    5. 高效算法:基于SSD框架,提出了新的算法来计算SSMs,这些算法利用了线性SSM递归和二次对偶形式,实现了在训练和推理计算、内存使用以及利用现代硬件上的矩阵乘法单元方面的最优权衡。
    6. 架构设计:利用SSD框架,将Transformers的优化和系统技术转移到SSMs中,设计了新的架构Mamba-2,该架构在语言建模方面与Transformers竞争,同时在序列长度上具有更好的扩展性。
    7. 系统优化:SSD框架允许利用为Transformers开发的系统优化工作,例如张量并行性(TP)、序列并行性等,来提高SSMs的效率。
    8. 实验验证:通过实验验证了Mamba-2在语言建模、训练效率和多查询关联记忆任务上的性能。

    通过这些步骤,论文不仅在理论上建立了SSMs和注意力机制之间的联系,还开发了新的算法和架构,使得SSMs能够更高效地运行,并且能够与Transformers相竞争。

    Q: 论文做了哪些实验?

    A: 论文中进行了一系列的实验来验证所提出的Mamba-2架构和SSD算法的有效性。以下是实验的主要内容:

    1. 合成关联记忆任务(Associative Recall)
      • 使用了一个更难版本的MQAR(Multi-Query Associative Recall)任务,该任务要求模型记忆多个键值对关联,并在提示时正确完成。
      • 实验结果表明,Mamba-2在所有设置中表现良好,即使在控制状态大小(N=16)的情况下,也比Mamba-1有显著提升。
    2. 语言建模(Language Modeling)
      • 在标准的自回归语言建模任务上训练和评估Mamba-2架构,并与其他架构进行比较。
      • 使用了Pile数据集,并遵循了GPT-3的规格进行训练。
    3. 扩展性规律(Scaling Laws)
      • 对于不同大小的模型,从约125M到约1.3B参数,进行了扩展性规律的实验。
      • 实验结果表明,Mamba-2在性能(困惑度)、理论FLOPs和实际墙钟时间上与Mamba和Transformer++相当或更好。
    4. 下游评估(Downstream Evaluations)
      • 在多种流行的零样本评估任务上,比较了Mamba-2与其他知名开源模型的性能。
      • 实验结果显示,Mamba-2在各个任务上的表现与Pythia相当,甚至在某些情况下超过了Mamba。
    5. 混合模型(Hybrid Models)
      • 探索了将SSD层与MLP和注意力层结合的不同方式,以了解每种组件的好处。
      • 实验发现,大约10%的层是注意力层时表现最佳。
    6. 速度基准测试(Speed Benchmarks)
      • 对SSD算法的速度进行了基准测试,与Mamba的扫描实现和FlashAttention-2进行了比较。
      • SSD算法由于利用了GPU上的矩阵乘法单元,比Mamba的融合关联扫描快2-8倍,并且在序列长度为2K及以上时比FlashAttention-2更快。
    7. 架构消融研究(Architecture Ablations)
      • 对Mamba-2块的设计进行了消融研究,包括参数投影、额外的归一化层等。
      • 验证了并行投影节省参数并提高了性能,额外的归一化层也略微提高了性能并有助于训练稳定性。
    8. 多头结构(Head Structure)
      • 对Mamba-2架构中多头结构的选择进行了消融研究,包括多值(MVA)、多查询(MQA)、多键(MKA)和多头(MHA)模式。
      • 发现多值(MVA)模式表现最佳。
    9. 注意力核近似(Attention Kernel Approximations)
      • 对于SSD结合线性注意力文献中的各种核近似方法进行了消融研究。
      • 测试了不同的核激活函数,包括cosFormer、Random Feature Attention和Performer等。

    这些实验全面评估了Mamba-2架构和SSD算法在不同方面的表现,包括记忆能力、语言建模性能、扩展性、速度和架构设计选择。通过这些实验,论文展示了Mamba-2在多个任务上与现有模型相比具有竞争力或优越性。

    Q: 有什么可以进一步探索的点?

    A: 论文在第10节中提出了一些可能的研究方向,这些方向可以用来进一步探索和改进结构化状态空间模型(SSMs)和注意力机制。以下是一些主要的探索点:

    1. 提高SSMs的表达性:研究如何扩展SSMs以包含更复杂的动态,例如通过引入非线性或更复杂的状态转换。
    2. 改进算法效率:开发新的算法来进一步提高SSMs的计算效率,特别是在处理长序列时。
    3. 理论分析:深入研究SSMs和注意力机制的理论基础,包括它们的性质、稳定性和收敛性。
    4. 系统优化:探索如何将SSMs与现代硬件架构更有效地结合,以实现更好的性能和扩展性。
    5. 注意力机制的改进:研究如何改进注意力机制,以解决如“注意力下沉”(attention sink)等问题,并提高其效率。
    6. 结构化矩阵的进一步研究:研究其他类型的结构化矩阵,以及如何将它们应用于深度学习模型中,以提高效率和表达性。
    7. 模型压缩和加速:探索模型压缩技术,以减少SSMs和注意力模型的参数数量和计算复杂度。
    8. 跨领域应用:将SSMs和改进的注意力机制应用于其他领域,如计算机视觉、自然语言处理之外的任务。
    9. 模型解释性:提高SSMs和注意力模型的解释性,帮助理解模型的决策过程。
    10. 混合模型:研究如何将SSMs与Transformers或其他类型的模型结合,以创建混合模型,这些模型结合了各自的优点。
    11. 优化硬件实现:针对特定硬件平台(如GPU、TPU)优化SSMs和注意力机制的实现,以充分利用硬件资源。
    12. 模型泛化能力:研究SSMs和注意力模型在不同任务和数据集上的泛化能力,以及如何提高它们对新情况的适应性。

    这些探索点为未来的研究提供了丰富的方向,有助于推动深度学习模型在效率、表达性和应用范围上的进一步发展。

    Q: 总结一下论文的主要内容

    A: 这篇论文提出了一个名为结构化状态空间对偶(Structured State Space Duality, SSD)的理论框架,旨在建立结构化状态空间模型(SSMs)和注意力机制之间的联系。以下是论文的主要内容概述:

    1. 背景介绍:论文首先介绍了Transformers和SSMs在深度学习,尤其是语言建模方面的成功应用。尽管SSMs在小到中等规模上展现出与Transformers相媲美或更优的性能,但它们的开发似乎与Transformers的优化工作相脱节。
    2. SSD框架:作者提出了SSD框架,通过结构化矩阵的概念,展示了SSMs和注意力机制之间的理论联系。这一框架允许将Transformers的算法和系统优化转移到SSMs中。
    3. SSMs与结构化矩阵:论文证明了SSMs与一类被称为半可分矩阵的结构化矩阵等价。这种联系揭示了SSMs的新属性和算法。
    4. 线性注意力理论的改进:作者改进了线性注意力的理论,并将其推广到一个新的结构化掩蔽注意力(Structured Masked Attention, SMA)家族。
    5. SSMs与SMA的联系:展示了SSMs和SMA之间存在大量交集,并且这些模型是彼此的对偶,具有SSM类的线性和注意力类的二次形式。
    6. 高效算法:论文提出了新的SSD算法,该算法利用半可分矩阵的块分解,实现了在所有主要效率轴上的最佳权衡。
    7. 架构设计:利用SSD框架,作者设计了新的架构Mamba-2,该架构在语言建模方面与Transformers竞争,同时在序列长度上具有更好的扩展性。
    8. 系统优化:SSD框架允许利用为Transformers开发的系统优化工作,如张量并行性和序列并行性,来提高SSMs的效率。
    9. 实验验证:论文通过实验验证了Mamba-2在语言建模、训练效率和多查询关联记忆任务上的性能。
    10. 相关工作和未来方向:论文最后讨论了与SSD框架相关的工作,并提出了未来研究的可能方向。

    总的来说,这篇论文通过提出SSD框架,为理解和改进序列模型提供了新的视角,并展示了如何将SSMs和注意力机制的优势结合起来,以构建性能更优、扩展性更好的深度学习模型。

  • 循环神经网络:消失和爆炸梯度并非全部故事

    循环神经网络(RNN)长期以来一直是建模时序数据的经典架构,但它们在训练长序列时却臭名昭著地难以训练。这是因为随着时间的推移,向后传播的误差信号往往会消失或爆炸。近年来,状态空间模型(SSM)作为 RNN 的一个子类,成功地克服了这些困难,这挑战了我们对 RNN 的理论理解。

    本文深入探讨了 RNN 的优化挑战,发现随着网络记忆的增加,其参数的变化会导致输出变化越来越大,使得基于梯度的学习高度敏感,即使没有爆炸梯度。我们的分析进一步揭示了逐元素递归设计模式与精心参数化的重要性,它们可以减轻这种影响。这种特性存在于 SSM 中,也存在于其他架构中,例如 LSTM。总体而言,我们的见解为 RNN 基于梯度学习的一些困难提供了一个新的解释,以及为什么某些架构比其他架构表现更好的原因。

    消失和爆炸梯度:一个老问题

    循环神经网络的训练难点在于,随着时间的推移,误差信号会不断累积,导致梯度要么消失,要么爆炸。这就像一个传声筒,声音在传递过程中不断衰减或放大。

    公式说明:

    假设一个 RNN 的隐藏状态为 ht,更新函数为 fθ,参数为 θ,输入序列为 (xt)t。网络的平均性能由损失函数 L 来衡量。

    ht+1 = fθ(ht, xt+1)
    L = E[ (1/T. * Σ(t=1 to T) Lt(ht) ]

    其中,Lt 表示在时间步 t 的损失。

    梯度消失和爆炸的根源:

    梯度消失和爆炸的根本原因在于,在反向传播过程中,误差信号会乘以一个称为雅可比矩阵(Jacobian matrix)的矩阵。如果该矩阵的特征值小于 1,误差信号就会不断衰减,导致梯度消失;反之,如果特征值大于 1,误差信号就会不断放大,导致梯度爆炸。

    解决方法:

    为了解决这个问题,人们提出了各种方法,例如:

    • LSTM 和 GRU: 这些架构引入了记忆单元,可以存储和检索信息,并通过特殊的门控机制控制误差信号的流动,从而避免梯度消失或爆炸。
    • 梯度裁剪: 将梯度的大小限制在一个范围内,防止其过大或过小。
    • 活动归一化: 通过对神经元的活动进行归一化,使梯度保持在一个合理的范围内。
    • 权重初始化: 使用合适的权重初始化方法,可以避免梯度消失或爆炸。
    • 架构约束: 通过对网络结构进行约束,例如使用分层处理、正交权重矩阵或振荡机制,可以改善梯度传播。

    记忆的诅咒:一个新挑战

    传统的观点认为,解决消失和爆炸梯度问题就足以让 RNN 学习长期依赖关系。然而,我们发现,即使解决了这些问题,RNN 仍然面临着一个新的挑战:记忆的诅咒。

    直观解释:

    RNN 的更新函数 fθ 会反复应用,这意味着修改参数 θ 不仅会影响一次更新,还会影响所有未来的更新。随着网络记忆的增加,隐藏状态会保留更多更新的影响,从而变得对参数变化更加敏感。

    数学分析:

    我们可以通过分析隐藏状态 ht 对参数 θ 的敏感度来理解记忆的诅咒:

    dht/dθ = Σ(t'≤t) dht/dht' * ∂fθ/∂θ(ht'-1, xt')

    当网络记忆更长时,雅可比矩阵 dht’/dht 的非零项数量会增加,导致敏感度增加。

    线性对角循环神经网络的分析:

    为了更深入地理解记忆的诅咒,我们分析了线性对角循环神经网络,其更新函数为:

    ht+1 = λ ⊙ ht + xt+1

    其中,λ 是一个向量,⊙ 表示逐元素乘积。

    结论:

    我们发现,随着 |λ| 接近 1,隐藏状态和反向传播误差的方差都会爆炸。这意味着,当网络试图编码更长的依赖关系时,其对参数变化的敏感度会急剧增加,从而阻碍学习过程。

    减轻记忆的诅咒:解决方案

    为了减轻记忆的诅咒,我们可以采用以下方法:

    • 输入归一化: 通过对输入进行归一化,可以控制隐藏状态的方差,避免其爆炸。
    • 特征值重新参数化: 通过对 λ 进行重新参数化,可以控制梯度的大小,避免其爆炸。

    状态空间模型和门控 RNN:

    状态空间模型和门控 RNN 隐含地包含了这些机制。状态空间模型通过对连续时间微分方程进行离散化来实现,而门控 RNN 通过门控机制来控制信息的流动。

    线性教师-学生分析:一个简单模型

    为了验证我们的理论,我们设计了一个线性教师-学生任务,其中教师和学生都遵循线性循环动态。

    结果:

    我们发现,当教师编码更长的依赖关系时,线性 RNN 难以学习,而具有对角连接的 LRU 则表现良好。

    对自适应学习率的重要性:

    我们还发现,对角连接可以简化损失函数的结构,使自适应学习率优化器能够更好地处理记忆的诅咒。

    深度循环神经网络的初始化信号传播:实际验证

    为了验证我们的理论在实际应用中的有效性,我们分析了深度循环神经网络在初始化时的信号传播。

    结果:

    我们发现,在初始化时,复杂对角 RNN 会受到记忆的诅咒的影响,而 LRU 则可以有效地减轻这种影响。

    结论

    消失和爆炸梯度并非循环神经网络训练的全部故事。记忆的诅咒是一个新的挑战,它源于 RNN 的迭代性质,并在网络接近动态稳定性边缘时出现。重新参数化和自适应学习率可以有效地减轻这种行为,而对角化递归可以简化这两个过程。

    我们的分析还揭示了学习复数特征值的复杂性,这可能解释了为什么最近大多数状态空间模型架构中没有发现复数有用。

    未来研究方向:

    理解如何提高小型线性模块的表达能力,同时保持其优良的优化特性,是一个很有前景的研究方向。


    Q: 这篇论文试图解决什么问题?

    A: 这篇论文探讨了循环神经网络(Recurrent Neural Networks, RNNs)在学习长期记忆时面临的挑战,尤其是梯度消失和梯度爆炸问题。尽管这些问题已经得到了广泛的研究,但论文指出,即使解决了这些问题,RNNs 在学习过程中仍然会遇到新的困难,即所谓的“记忆的诅咒”(curse of memory)。具体来说,论文的主要贡献和解决的问题包括:

    1. 记忆的诅咒:论文提出了一个新的问题,即随着网络记忆能力的增加,网络活动对其参数变化的敏感度也在增加,这使得基于梯度的学习变得非常敏感,即使在没有梯度爆炸的情况下也是如此。
    2. 状态空间模型(SSMs)的有效性:论文探讨了状态空间模型(一种特殊的 RNN),这类模型在捕捉长期依赖性方面表现出色。作者试图分析这种有效性是否可以完全归因于它们避免了梯度消失和爆炸。
    3. 元素级递归设计模式的重要性:论文分析了元素级递归设计模式与精心设计的参数化在减轻记忆诅咒效应中的重要性。
    4. RNNs 的优化挑战:论文深入探讨了 RNNs 的优化问题,并提出了一些新的见解,解释了为什么某些架构(如 LSTMs 和 SSMs)在基于梯度的学习中表现更好。
    5. 理论分析与实验验证:论文不仅在理论上分析了上述问题,还通过简单的教师-学生任务和深度循环网络的实验来验证理论分析的正确性。

    总的来说,论文试图提供一个更全面的视角来理解 RNNs 在学习长期依赖性时面临的挑战,并探讨了除了梯度消失和爆炸之外的其他复杂性。

    Q: 有哪些相关研究?

    A: 论文中提到了多个与循环神经网络(RNNs)及其训练挑战相关的研究领域和具体工作。以下是一些主要的相关研究和它们的贡献:

    1. 梯度消失和梯度爆炸问题
      • Hochreiter (1991) [5], Bengio et al. (1994) [6], Hochreiter et al. (2001) [7] 等研究者早期工作强调了在时间反向传播过程中梯度消失或爆炸的困难。
      • Pascanu et al. (2013) [8] 对此问题进行了进一步的分析。
    2. 解决梯度问题的尝试
      • LSTM (Long Short-Term Memory) 单元 [3] 和 GRU (Gated Recurrent Unit) [23] 通过使用记忆神经元来促进直接信息存储和检索,从而解决了这个问题。
    3. 其他方法
      • 梯度裁剪 [24, 8], 活动归一化 [25–27], 谨慎的权重初始化 [28, 29] 或者架构约束(如分层处理 [30, 31], 正交权重矩阵 [32–34] 和振荡 [35–37])。
    4. 注意力机制和 Transformer
      • Bahdanau et al. (2015) [9] 提出了用于神经机器翻译的注意力机制。
      • Vaswani et al. (2017) [10] 提出了 Transformer 模型,它通过直接的 token-to-token 通信简化了长序列信号传播。
    5. Transformer 的效率改进
      • 一系列研究 [11–15] 旨在使 Transformer 更加高效,包括更快的自回归 Transformer [12], FlashAttention [13], Switch Transformers [14] 等。
    6. 状态空间模型 (SSMs)
      • 一系列研究 [16–22] 探讨了状态空间模型,这是一种新型的线性循环网络,以其捕捉长期依赖性的能力而著称。
    7. 优化和学习率理论
      • 有关学习率和优化的理论研究,例如 Noci et al. (2024) [41] 和 Yang et al. (2020) [42]。
    8. 深度学习中的信号传播
      • 有关深度神经网络中信号传播的研究,例如 Chen et al. (2018) [50] 和 Cohen et al. (2020) [54]。
    9. 在线学习和组合泛化
      • 有关在线学习 [58] 和组合泛化 [59] 的研究。

    这些研究为理解 RNNs 的训练挑战、改进它们的性能以及开发新的架构提供了理论基础和实证支持。论文通过引用这些相关工作,建立了其研究的理论背景和实践意义。

    Q: 论文如何解决这个问题?

    A: 论文通过以下几个步骤来解决循环神经网络(RNNs)在学习长期记忆时遇到的问题:

    1. 理论分析:首先,论文对RNNs的优化挑战进行了深入的理论分析,特别是针对“记忆的诅咒”(curse of memory)这一新问题。作者探讨了随着网络记忆的增加,网络活动对参数变化的敏感度如何增加,以及这如何影响梯度的学习。
    2. 状态空间模型(SSMs):论文研究了SSMs这一特殊类型的RNN,这些模型通过避免梯度消失和爆炸问题,以及采用元素级递归设计模式和精心设计的参数化,有效地捕捉长期依赖性。
    3. 元素级递归设计模式:论文强调了元素级递归设计模式的重要性,这种设计模式结合了精心的参数化,可以减轻记忆诅咒效应。
    4. 教师-学生任务分析:通过一个简单的教师-学生任务,论文展示了线性循环网络的学习复杂性,并分析了所提出的解决方案在这种简单设置中的有效性。
    5. 实验验证:论文通过实验验证了理论分析的正确性。作者在深度循环网络中研究了初始化时的信号传播,并比较了不同循环层(如复杂对角线RNN、LRU和LSTM)的性能。
    6. 参数化和归一化策略:论文提出了特定的参数化和归一化策略,如输入归一化和特征值重参数化,以控制隐藏状态和梯度的幅度,从而减轻记忆诅咒的影响。
    7. 适应性学习率优化器:论文指出,适应性学习率优化器(如Adam)可以有效地解决由于记忆诅咒导致的额外敏感性问题,特别是当这些敏感性集中在少数几个参数上时。

    通过这些方法,论文不仅提供了对RNNs训练难题的新解释,还揭示了为什么某些架构(如LSTMs和SSMs)在基于梯度的学习中表现更好,并为设计更易于优化的RNN架构提供了指导。

    Q: 论文做了哪些实验?

    A: 论文中进行了一系列实验来验证理论分析,并探讨循环神经网络(RNNs)在学习和优化过程中的行为。以下是论文中提到的主要实验:

    1. 线性教师-学生任务(Linear Teacher-Student Task)
      • 这是一个简化的设置,用于直观展示记忆诅咒和梯度消失问题是如何相互作用的。
      • 作者考虑了一个一维动力学的学生模型,其目标是复现具有复数参数λ*的教师模型的隐藏状态。
      • 通过改变λ*的值,研究了在不同记忆长度下,学生模型学习教师行为的难度。
    2. 对角连接简化优化(Diagonal Connectivity Simplifies Optimization)
      • 实验中,作者比较了线性RNN和具有对角连接的LRU(Linear Recurrent Unit)在学习长期依赖性时的性能差异。
      • 通过调整教师模型的记忆长度(通过控制隐藏维度n和变换参数ν),观察了学生模型在复现教师行为时的困难程度。
    3. 自适应学习率的重要性(Importance of Adaptive Learning Rates)
      • 研究了直接参数化循环连接矩阵的复杂特征值如何影响损失景观,并使具有自适应学习率的优化器能够补偿这些行为。
      • 通过分析损失函数的Hessian矩阵,展示了在优化过程中,自适应学习率如何帮助处理额外的敏感性。
    4. 深度循环网络中的信号传播(Signal Propagation in Deep Recurrent Networks at Initialization)
      • 在深度循环网络中,研究了在初始化时信号如何在网络中传播,以及记忆参数ν如何影响隐藏状态和梯度的方差。
      • 比较了不同类型的循环层(如复杂对角线RNN、LRU和LSTM)在信号传播和梯度爆炸方面的表现。
    5. 损失景观的结构分析(Structure of the Loss Landscape)
      • 通过可视化和理论分析,研究了在优化过程中损失景观的结构,特别是在对角化和非对角化情况下的差异。
    6. 集中特征值分布的影响(Impact of Concentrating Eigenvalue Distributions)
      • 探讨了当特征值分布集中时,对学习动态的影响,特别是在对角RNN和LRU架构中。

    这些实验涉及了理论分析、数值模拟和可视化,旨在深入理解RNNs在学习和优化过程中的复杂性,并验证作者提出的理论观点。通过这些实验,论文展示了记忆诅咒问题的存在,并提出了相应的解决方案。

    Q: 有什么可以进一步探索的点?

    A: 论文中提出了多个可以进一步探索的点,以下是一些主要的研究方向:

    1. 复杂参数化:论文讨论了对于复数参数的复杂参数化问题,特别是角度参数的困难。研究如何改进这种参数化,以便更好地优化和学习可能是一个有价值的方向。
    2. 自适应学习率优化器:论文指出自适应学习率优化器(如Adam)在处理敏感性方面表现出色。进一步研究这些优化器与RNN架构的相互作用,以及如何改进它们以更好地处理长期依赖性,可能是一个有前景的领域。
    3. 状态空间模型(SSMs):SSMs在捕捉长期依赖性方面表现出色,但它们的表达能力有限。研究如何增加SSMs的表达能力,同时保持它们的快速训练速度,可能是一个重要的研究方向。
    4. 深度循环网络的初始化:论文在初始化时研究了深度循环网络中的信号传播。探索不同的初始化策略,以及它们如何影响网络的训练动态和最终性能,可能是一个有价值的研究领域。
    5. 损失景观的结构:论文分析了损失景观的结构,特别是在对角化和非对角化情况下的差异。进一步研究这些结构如何影响优化过程,以及如何设计损失函数以促进更好的学习,可能是一个有趣的方向。
    6. 集中特征值分布的影响:论文探讨了集中特征值分布对学习动态的影响。研究如何管理这些分布,以及它们如何影响不同架构(如对角RNN和LRU)的性能,可能是一个值得探索的领域。
    7. 模块化和独立机制:论文提出了神经元和自适应学习率优化器之间的共生关系。研究如何设计具有模块化和独立机制的网络,以及这些设计如何促进在线学习、组合泛化和并行化,可能是一个有潜力的研究方向。
    8. 理论与实践的结合:论文中的一些理论分析和实验结果表明,理论与实践之间存在差距。进一步探索这种差距的原因,并开发能够弥合这一差距的方法和理论,可能是一个重要的研究领域。
    9. 长期依赖性学习:尽管论文讨论了长期依赖性学习的一些挑战,但这一领域仍然存在许多未解决的问题。研究新的架构和技术,以更有效地学习长期依赖性,可能是一个持续的研究主题。

    这些探索点为未来的研究提供了丰富的思路,有助于推动循环神经网络和深度学习领域的进一步发展。

    Q: 总结一下论文的主要内容

    A: 这篇论文深入探讨了循环神经网络(Recurrent Neural Networks, RNNs)在学习长期依赖性时面临的挑战,并提出了新的见解和解决方案。以下是论文的主要内容总结:

    1. 问题背景:RNNs 在学习长期记忆时,由于梯度消失和梯度爆炸问题,训练变得非常困难。尽管现有研究已经提出了多种解决这些问题的方法,但论文指出,即使这些问题得到解决,RNNs 仍然面临其他挑战。
    2. 记忆的诅咒:论文提出了一个新的问题——“记忆的诅咒”。随着网络记忆的增加,网络活动对参数变化的敏感度也在增加,这使得基于梯度的学习变得非常敏感。
    3. 状态空间模型(SSMs):论文研究了状态空间模型,这是一类特殊的 RNN,它们能够有效地捕捉长期依赖性。作者探讨了 SSMs 的成功是否可以完全归因于它们避免了梯度消失和爆炸。
    4. 元素级递归设计模式:论文强调了元素级递归设计模式的重要性,并指出结合精心设计的参数化可以减轻记忆诅咒效应。
    5. 理论分析:论文通过理论分析,展示了 RNNs 在信号传播方面的行为,特别是在对角化和非对角化情况下的差异。
    6. 实验验证:通过一系列实验,包括线性教师-学生任务、深度循环网络的信号传播分析等,论文验证了理论分析的正确性,并展示了所提出解决方案的有效性。
    7. 自适应学习率优化器:论文指出,自适应学习率优化器(如 Adam)可以有效地解决由于记忆诅咒导致的额外敏感性问题。
    8. 损失景观结构:论文分析了损失景观的结构,特别是在对角化和非对角化情况下的差异,以及如何通过参数化和归一化策略来控制隐藏状态和梯度的幅度。
    9. 未来研究方向:论文提出了多个未来研究方向,包括改进参数化、优化器与架构的相互作用、损失函数设计、初始化策略等。
    10. 结论:论文得出结论,虽然梯度消失和爆炸问题是 RNNs 训练中的挑战,但解决这些问题并不足以确保良好的学习性能。通过揭示新的挑战和解决方案,论文为理解和改进 RNNs 的训练提供了新的视角。

    总的来说,这篇论文通过理论分析和实验验证,为理解和解决 RNNs 在学习长期依赖性时面临的挑战提供了深入的见解,并提出了一系列有价值的研究方向。


  • 深入探讨循环神经网络:消失和爆炸梯度并非故事的终点

    深入探讨循环神经网络:消失和爆炸梯度并非故事的终点

    循环神经网络(RNNs)长期以来一直是处理时间序列数据的经典架构。然而,RNN在长序列训练中面临的一个主要难题就是梯度的消失和爆炸。尽管近年来状态空间模型(SSMs),作为RNN的一种子类,成功解决了这些问题,但它们的成功却挑战了我们对理论的理解。本文将深入探讨RNN的优化挑战,并发现随着网络记忆的增加,参数变化会导致输出变化剧烈,即使没有梯度爆炸,基于梯度的学习仍然非常敏感。我们的分析还揭示了元素级递归设计模式结合细致参数化在缓解这一问题中的重要性。这一特性存在于SSMs以及其他架构,如LSTM中。总体而言,我们的见解为RNN基于梯度学习的一些困难提供了新的解释,并解释了为什么某些架构比其他架构表现更好。

    循环神经网络的挑战

    循环神经网络(RNN)在处理时间序列数据方面表现出色,但它们在训练长序列时面临显著挑战,主要是因为误差信号在时间反向传播过程中会消失或爆炸。注意力机制(如Transformer中所用)通过允许直接的token-to-token通信,极大地简化了长时间间隔内的信号传播,解决了这些问题。然而,Transformer的性能提升伴随着计算和内存消耗的增加,这激发了大量研究以提高其效率。

    一种有前景的研究方向是线性递归网络的新类型,即状态空间模型(SSMs)。这些模型以更快的训练速度换取表达能力的降低,并已被证明在捕捉长程依赖性方面特别有效。我们在本文中探讨这种有效性是否仅仅归因于它们避免了梯度消失和爆炸。由于这些模型的简单性,它们为深入的理论分析提供了机会。我们重点研究这些模型中的信号传播。

    在回顾经典的RNN结果后,我们发现它们会遭遇一个被忽视的问题:记忆的诅咒。当递归网络编码更长的记忆时,网络活动对参数变化变得极其敏感,即使网络动态保持稳定。在第三部分,我们展示了SSMs及其他架构如LSTM如何有效地缓解这一问题。最后,我们通过分析一个简单的教师-学生任务,揭示了线性递归网络学习的复杂性,并讨论了我们的发现如何扩展到更现实的场景。

    消失和爆炸梯度问题

    首先介绍我们将在本文中使用的符号。我们考虑一个具有隐藏状态 $h_t$ 的循环神经网络,更新函数 $f_\theta$ 由参数 $\theta$ 参数化,以及输入序列 $(x_t)_t$。网络的平均性能通过损失 $L$ 来衡量。我们有:

    $$
    h_{t+1} = f_\theta(h_t, x_{t+1}) \quad \text{和} \quad L = \mathbb{E} \left[ \sum_{t=1}^T L_t(h_t) \right]
    $$

    瞬时损失 $L_t$ 相对于参数 $\theta$ 的梯度等于:

    $$
    \frac{dL_t}{d\theta} = \frac{\partial L_t}{\partial h_t} \frac{dh_t}{d\theta} = \frac{\partial L_t}{\partial h_t} \sum_{t’ \le t} \frac{dh_t}{dh_{t’}} \frac{\partial f_\theta}{\partial \theta} (h_{t’-1}, x_{t’})
    $$

    早期研究指出,梯度下降很难让RNN记住将来有用的过去输入,因为误差信号在时间反向传播过程中要么消失要么爆炸。关键的量是:

    $$
    \frac{dh_t}{dh_{t’}} = \prod_{i=t’}^{t-1} \frac{\partial h_{i+1}}{\partial h_i} = \prod_{i=t’}^{t-1} \frac{\partial f_\theta}{\partial h}(h_i, x_{i+1})
    $$

    当雅可比矩阵 $\frac{\partial h_{i+1}}{\partial h_i}$ 的谱半径小于1时,这个量会指数级收敛到0;如果存在大于1的分量,它会指数级爆炸。随着时间跨度的增加,过去隐藏状态对当前损失的贡献变得要么可忽略不计,要么占主导地位,这使得基于梯度的长期记忆学习变得困难。

    记忆的诅咒

    解决了消失和爆炸梯度问题后,RNN是否就能顺利学习长程依赖了呢?我们发现并非如此。即使动态稳定,随着网络记忆的增加,梯度仍可能爆炸。

    直观理解

    RNN的特殊之处在于同一个更新函数 $f_\theta$ 被反复应用。因此,修改参数不仅影响单次更新,而是所有更新。随着网络记忆的增加,隐藏状态对参数变化变得越来越敏感,即使没有梯度爆炸,学习参数仍变得更加困难,这就是记忆的诅咒。

    线性对角递归神经网络中的信号传播

    我们研究了在编码长程依赖时,隐藏状态和梯度幅度如何演变。理想情况下,这些量不应消失或爆炸。我们做了以下假设:

    1. 线性对角递归神经网络:我们限制更新函数为$f_\theta(h_t, x_{t+1}) = \lambda \odot h_t + x_{t+1}$,其中$\lambda$是与$h_t$同维的向量,$\odot$表示元素级乘积。
    2. 无限时间视角:考虑无限序列,并在$t_0 = -\infty$初始化网络动态。
    3. 广义平稳:假设网络接收的不同量(包括输入$x_t$)是广义平稳的,即自相关函数与时间无关。

    在这些假设下,我们分析了单层递归网络中的信号传播,发现当$|λ| \to 1$时,隐藏状态和反向传播的误差都会爆炸。

    缓解记忆的诅咒

    给定这一问题,如何缓解呢?对角连接的递归网络特别适合。除了避免梯度爆炸,它们还通过输入归一化和重新参数化来缓解记忆的诅咒。

    解决方案:归一化和重新参数化

    通过引入输入归一化和重新参数化,我们可以保持隐藏状态和梯度的幅度稳定。例如,为了保持$E[h_t^2]$和$E[(d_\lambda h_t)^2]$独立于$\lambda$,我们可以引入一个归一化因子$\gamma(\lambda)$,并选择适当的参数化方式来控制$\lambda$。

    复杂数的情况

    对于复杂数$\lambda$,合适的参数化更加困难。我们的分析表明,若$\lambda$参数化为$\nu \exp(i\theta)$,则$\theta$的参数化必须依赖于$\nu$,但反之不然。尽管如此,这种参数化并不会妨碍学习。

    多种RNN架构的比较

    状态空间模型和门控RNN都具有某种形式的归一化和重新参数化机制,有助于信号传播。我们比较了这些机制在不同架构中的作用,发现状态空间模型和门控RNN在缓解记忆的诅咒方面表现出色。

    线性教师-学生分析

    我们考虑一个教师-学生任务,教师和学生都是线性递归网络。尽管这是最简单的设置,但它揭示了RNN学习中的复杂性。通过一维和多维情况的实验,我们发现对角化显著简化了优化过程,并且自适应学习率对缓解记忆的诅咒至关重要。

    自适应学习率的重要性

    自适应学习率可以有效应对梯度的爆炸。我们分析了损失函数的Hessian矩阵,发现对角化结构有助于自适应优化器更好地处理较大的曲率,从而加速学习。

    深度递归网络中的信号传播

    我们进一步验证了理论趋势在实际中的适用性。通过在深度递归网络中初始化信号传播,实验结果验证了复杂数RNN、LRU和LSTM在不同记忆长度下的表现。我们发现LRU在前向和反向传递中几乎完全缓解了记忆的诅咒,而LSTM则通过特定参数化保持了梯度的稳定。

    结论

    梯度消失和爆炸使得RNN的学习变得复杂,但解决这些问题并非终点。我们发现,RNN的迭代特性在动态稳定的边缘引发了另一个学习困难。通过重新参数化和自适应学习率可以有效缓解这一问题,而对角化递归简化了优化过程。我们的分析还揭示了学习复杂数特征的复杂性,这可能解释了为什么复杂数在最新的状态空间模型架构中并不常见。

    未来研究可以进一步探索如何在保持良好优化特性的同时,提高小型线性模块的表达能力。理解模块化设计在不同场景中的应用,可能会为构建更高效和强大的神经网络提供新的思路。

人生梦想 - 关注前沿的计算机技术 acejoy.com