Reading

状态空间模型SSM到Mamba

概述

SSM的概念由来已久,但这里我们特指深度学习中的SSM,一般认为其开篇之作是2021年的 S4,不算太老,而SSM最新最火的变体大概是Mamba。当然,当我们谈到SSM时,也可能泛指一切线性RNN模型,这样RWKVRetNet还有此前LRU都可以归入此类。不少SSM变体致力于成为Transformer的竞争者,尽管笔者并不认为有完全替代的可能性,但SSM本身优雅的数学性质也值得学习一番。

尽管我们说SSM起源于S4,但在S4之前,SSM有一篇非常强大的奠基之作《HiPPO: Recurrent Memory with Optimal Polynomial Projections》(简称HiPPO),所以本文从HiPPO开始说起。

另外值得一提的是,SSM代表作HiPPO、S4、Mamba的一作都是Albert Gu,他还有很多篇SSM相关的作品,毫不夸张地说,这些工作筑起了SSM大厦的基础。不论SSM前景如何,这种坚持不懈地钻研同一个课题的精神都值得我们由衷地敬佩。

今天,基本上你能叫出的任何语言模型都是 Transformer 模型。OpenAI 的 ChatGPT、谷歌的 Gemini 和 GitHub 的 Copilot 等都是由 Transformer 驱动的,仅举几个例子。然而,Transformer 存在一个基本缺陷:它们由 Attention 驱动,其扩展速度与序列长度呈二次方关系。简单来说,对于快速交流(让 ChatGPT 讲一个笑话),这没问题。但对于需要大量文字的查询(让 ChatGPT 总结一份 100 页的文档),Transformer 可能会变得过于缓慢。

SSM系列就是这种线性时间语言模型

状态空间模型(State Space Model, SSM)

什么是状态空间?

状态空间包含完全描述系统所需的最小变量数。它是通过定义系统的可能状态来数学地表示问题的方法。

让我们稍微简化一下。想象我们正在迷宫中导航。 “状态空间”是所有可能位置(状态)的地图。每个点代表迷宫中一个独特的位置,具有特定的细节,比如你离出口有多远。

“状态空间表示”是对此图的简化描述。它显示了当前的位置(当前状态)、可以前往的地方(可能的未来状态),以及哪些变化能进入下一个状态(向右或向左)。

描述状态的变量,在我们的例子中是 \(X\)\(Y\) 坐标,以及到出口的距离,可以表示为“状态向量”。

image

听起来熟悉吗?这是因为语言模型中的嵌入或向量也常被用来描述输入序列的“状态”。例如,你当前位置(状态向量)的向量可能看起来有点像这样:

image

在神经网络中,系统的“状态”通常是其隐藏状态,在大型语言模型的背景下,生成新标记最重要的方面之一。

什么是状态空间模型?

SSMs 是用于描述这些状态表示并基于某些输入预测其下一个状态可能是什么的模型。

传统上,在时间 \(t\),状态空间模型(SSMs):

  • 将输入序列 \(x(t)\) 进行映射(例如,在迷宫中向左下方移动)
  • 转换为潜在状态表示 \(h(t)\)(例如,距离出口和 \(x/y\) 坐标)
  • 并推导出预测输出序列 \(y(t)\)(例如,再次向左移动以更快到达出口)

然而,它不是使用离散序列(如向左移动一次)作为输入,而是接受一个连续序列并预测输出序列。

image

SSMs假设动态系统(如 3D 空间中移动的物体),可以通过下面两个方程对其在时间 \(t\) 的状态进行预测。

image

我们希望通过数学方程,把系统的历史状态和输入信号,转换成一种“状态表示” \(h(t)\)。有了这个 \(h(t)\),我们就能根据输入信号,预测系统接下来会怎么变化、会输出什么结果。

image

这两个方程是状态空间模型的核心。其中,\(h(t)\) 指的是在任意给定时间 \(t\) 的潜在状态表示,而 \(x(t)\)指的是某些输入。

  • 状态方程
image.png

在控制理论和动态系统中,状态方程用于刻画系统状态如何随时间变化。具体来说,它强调了两个重要的矩阵:

    1. 矩阵A:这个矩阵描述了系统的状态如何自然演变,也就是在没有外部输入的情况下,系统的内部动态特性。
    2. 矩阵B:这个矩阵则描述了输入如何影响系统的状态。它衡量了输入信号对系统状态变化的贡献。

总的来说,状态方程结合了这两个矩阵,反映了系统的动态行为以及外部输入如何影响这些行为。

  • 输出方程描述了状态是如何通过矩阵 C 转换为输出,以及输入是如何通过矩阵 D 影响输出的。这里同样有两个重要的矩阵
    1. 矩阵C:决定了系统内部状态是如何“投影”或“变换”成我们能看到的输出的。
    2. 矩阵D:描述了输入信号有没有可能直接影响输出(比如输入一来,输出马上就受影响)。
image

状态空间模型架构

可视化这两个方程,我们得到以下架构:

image

让我们一步一步地了解这些矩阵如何影响学习过程。

  • 假设我们有一些输入信号 \(x(t)\),这个信号首先被矩阵 \(B\) 乘以,该矩阵描述了输入如何影响系统。
image.png
  • 更新后的状态(类似于神经网络的隐藏状态)是一个包含环境核心“知识”的潜在空间。我们用矩阵 \(A\) 乘以状态,矩阵 \(A\) 描述了所有内部状态如何连接,因为它们代表了系统的潜在动态。
image.png
  • 然后,我们使用矩阵 \(C\) 来描述状态如何转换为输出。
image.png
  • 最后,我们可以利用矩阵 \(D\) 直接从输入到输出提供信号。这通常也被称为残差连接。
image.png
  • SSM 通常被视为以下形式,而不包含残差连接。
image.png

从连续信号到离散信号

在自然界或许多工程系统中,信号随时间连续变化(如温度、速度等)。但在实际应用(如文本序列、数字采样)中,我们通常只在特定时刻获得输入数据。如果系统模型(如状态空间模型,SSM)是连续的,而输入却是离散的,那么直接分析和计算就很困难。

为了做到这一点,我们使用零阶保持(*Zero-order hold *)技术。它把离散输入“变成”连续信号,使得连续时间的模型可以处理。具体来说,每当收到一个新的离散输入,就把这个值“保持”到下一个输入到来为止。这样得到一个阶梯状的连续信号,如下图所示

image

保持每个输入值的时间长度称为“步长”,记作 \(\Delta\)。决定了离散化的“分辨率”,也可以设为可学习的参数,让模型自动适应最优的采样频率。

现在我们有了连续的输入信号,我们可以生成连续的输出,对于输出可以根据输入的时间步长采样就变成了离散的输出

image

从数学上讲,我们可以将零阶保持应用到状态转移矩阵 \(A\) 和输入矩阵 \(B\) 也需要离散化,变成适用于离散时间的参数。

image

原本是 \(x(t) \rightarrow y(t)\),即输入和输出都是时间的连续函数。现在我们通过Zero-order hold,把输入和输出都变成了序列 \(x_k \rightarrow y_k\)\(k\) 表示离散时间步。

image

矩阵 \(A\) 的重要性

毫无疑问,SSM 公式的最重要方面之一是矩阵 A。它捕捉了先前的状态信息以构建新的状态。

image

从本质上讲,矩阵 \(A\) 产生了隐藏状态:

image

矩阵 \(A\) 决定了当前状态如何由前一个状态演化而来。\(A\) 的设计直接影响了模型对历史信息的“记忆长度”和“记忆方式”。

  • 如果 \(A\) 设计得比较“短视”,比如只有最近的几个状态有影响,那么模型只能记住最近的几个 token(输入)。
  • 如果 \(A\) 设计得“长视”或者有特殊结构(比如对角化、低秩、带有遗忘门等),就可能让模型把所有历史 token的信息都融入到当前状态中,实现“长时记忆”。

总的来说,\(A\) 决定了模型记忆历史的能力。那么我们如何创建矩阵 \(A\) 以保留大量记忆(上下文大小)呢?

HiPPO

我们可以使用HIPPO,即高阶多项式投影算子。HiPPO 试图将其迄今为止看到的所有输入信号压缩成一个系数向量。

详情可以可以参考:SSM奠基之作-HiPPO

S4(Structured State Space for Sequences)

S4是对HiPPO的进一步补充和完成,它的关键一笔是提出了 \(A\) 等价于“对角+低秩”的矩阵形式,为剩余部分的分析奠定了基础。因为一开始 \(A\) 是分段定义的形式,而不是矩阵运算形式,这样的定义不利于应用现有的线性代数工具进行一般化分析。

S4 是一种最近出现的状态空间模型(SSM)架构。在这里,只是总结其中的重要部分,但如果想要更深入地了解 S4,可以阅读重温SSM(三):HiPPO的高效计算(S4)The Annotated S4

从高层次来看,S4 学习如何通过一个中间状态 \(h(t)\) 将输入 \(x(t)\) 映射到输出 \(y(t)\) 。在这里, \(x\) 、 \(y\) 和 \(h\) 是 \(t\) 的函数,因为 SSM 被设计成连续数据(如音频、传感器数据和图像)可以比较好的工作。S4 通过三个连续参数矩阵 \(A\) 、 \(B\) 和 \(C\) 将它们联系起来。这些参数通过以下两个方程相互关联:

\[\begin{aligned}h'(t)&=\mathbf{A}h(t)+\mathbf{B}x(t)\\y(t)&=\mathbf{C}h(t)\end{aligned}\]

在实践中,我们总是处理离散数据,如文本。这需要我们将 SSM 离散化,通过使用

\(Δ\)将连续参数 \(\mathbf{A}\)\(\mathbf{B}\)\(\mathbf{C}\) 转换为离散参数 \(\mathbf{\bar{A}}\)\(\mathbf{\bar{B}}\)\(\mathbf{C}\)。一旦离散化,我们就可以通过以下两个方程来表示 SSM:

\[\begin{aligned}h_t&=\mathbf{\bar{A}}h_{t-1}+\mathbf{\bar{B}}x_t\\y_t&=\mathbf{C}h_t\end{aligned}\]

这些方程构成了一个递归,类似于在循环神经网络(RNN)。在每一步 \(t\) ,我们将前一个时间步的隐藏状态 \(h_{t−1}\) 与当前输入 \(x_t\) 结合,以创建新的隐藏状态 \(h_t\) 。下面这张图显示了如何预测句子中的下一个单词(在这种情况下,预测“and”跟在“My name is Jack”后面)。

image.png

以这种方式,我们实际上可以将 S4 作为 RNN 一次生成一个标记。然而,S4 真正酷的地方在于你还可以将其用作卷积神经网络(CNN)。在上面的例子中,让我们看看当我们把之前提到的离散方程扩展到尝试计算 \(h_3\) 时会发生什么。为了简单起见,让我们假设 \(x_{−1}=0\) 。

\[\begin{aligned}h_0&=\mathbf{\bar{B}}x_0\\h_1&=\mathbf{\bar{A}}(\mathbf{\bar{B}}x_0)+\mathbf{\bar{B}}x_1\\h_2&=\mathbf{\bar{A}}(\mathbf{\bar{A}}(\mathbf{\bar{B}}x_0)+\mathbf{\bar{B}}x_1)+\mathbf{\bar{B}}x_2\\h_3&=\mathbf{\bar{A}}(\mathbf{\bar{A}}(\mathbf{\bar{A}}(\mathbf{\bar{B}}x_0)+\mathbf{\bar{B}}x_1)+\mathbf{\bar{B}}x_2)+\mathbf{\bar{B}}x_3\end{aligned}\]

计算 \(h_3\) 后,我们可以将其代入 \(y_3\) 的方程中,以预测下一个单词。

\[\begin{aligned}y_3&=\mathbf{C}(\mathbf{\bar{A}}(\mathbf{\bar{A}}(\mathbf{\bar{A}}(\mathbf{\bar{B}}x_0)+\mathbf{\bar{B}}x_1)+\mathbf{\bar{B}}x_2)+\mathbf{\bar{B}}x_3)\\y_3&=\mathbf{C\bar{A}\bar{A}\bar{A}\bar{B}}x_0+\mathbf{C\bar{A}\bar{A}\bar{B}}x_1+\mathbf{C\bar{A}\bar{B}}x_2+\mathbf{C\bar{B}}x_3\end{aligned}\]

注意到, \(y_3\) 实际上可以计算为一个点积,其中右边的向量只是我们的输入 \(x\) :

\[y_3=\begin{pmatrix}\mathbf{C\bar{A}\bar{A}\bar{A}\bar{B}} & \mathbf{C\bar{A}\bar{A}\bar{B}} & \mathbf{C\bar{A}\bar{B}} & \mathbf{C\bar{B}}\end{pmatrix}\begin{pmatrix}x_0\\x_1\\x_2\\x_3\end{pmatrix}\]

因为 \(\mathbf{\bar{A}}\), \(\mathbf{\bar{B}}\) 和 \(\mathbf{C}\) 都是常数,我们可以预先计算左端向量并将其保存为我们的卷积核 \(\mathbf{\bar{K}}\) 。这使得我们能够通过以下两个方程 轻松地通过卷积计算 \(y\) :

\[\begin{aligned}\mathbf{\bar{K}}&=\begin{pmatrix}\mathbf{C\bar{B}} & \mathbf{C\bar{A}\bar{B}} & \cdots & \mathbf{C\bar{A}^k\bar{B}}\end{pmatrix}\\y&=\mathbf{\bar{K}} * x\end{aligned}\]

重要的是,“RNN 模式”和“CNN 模式”,在数学上是等价的。这使得 S4 可以根据需要执行的任务进行变形,其输出没有差异。我们可以在 S4 论文的表 1 中比较这些“模式”之间的差异,该表显示了每种形式的训练和推理的运行时复杂度(粗体表示每个指标的最佳结果)。

image

可以注意到 CNN 模式更适合训练,而 RNN 模式更适合推理。在 CNN 模式下,我们可以利用并行性一次性训练多个示例。在 RNN 模式下,尽管我们一次只能计算一步,但每一步都需要相同的工作量。因为 S4 可以使用这两种模式,所以它实际上得到了两者的最佳结合:快速训练,以及更快的推理。

Mamba

现在我们可以探讨 Mamba 提出的第一个主要观点:选择性。让我们回顾一下定义 S4 离散形式的两个方程:

\[\begin{aligned}h_t&=\mathbf{\bar{A}}h_{t-1}+\mathbf{\bar{B}}x_t\\y_t&=\mathbf{C}h_t\end{aligned}\]

选择性

请注意,在 S4 中,离散参数  \(\mathbf{\bar{A}}\), \(\mathbf{\bar{B}}\) 和 \(\mathbf{C}\) 是常数。然而,Mamba 使这些参数根据输入而变化。我们最终会得到类似以下的内容:

\[\begin{aligned}h_t&=s_\mathbf{\bar{A}}(x_t)h_{t-1}+s_\mathbf{\bar{B}}(x_t)x_t\\y_t&=s_\mathbf{C}(x_t)h_t\end{aligned}\]

作者认为选择性,或者说输入依赖性,对于许多任务来说很重要。可以这样思考:因为 S4 没有选择性,它被迫对输入的所有部分进行完全相同的处理。然而,当你阅读一个句子时,一些词不可避免地比其他词更重要。想象一下,我们有一个根据意图对句子进行分类的模型,我们给它提供的句子是:“我想点一个汉堡。”如果没有选择性,S4 在处理每个词时都会花费相同的“努力”。点击下面的按钮,看看句子是如何逐个词被处理的。

image

但如果你是一个试图分类句子意图的模型,你可能更希望“关注”一些词而不是其他词。单词“想要”和“要”在句子的潜在含义中真正贡献了多少价值?实际上,如果我们能更多地用有限的脑力关注像“订单”这样的词,以了解用户想要做什么,以及“汉堡”这样的词,以了解用户在点什么,那就太好了。通过使模型参数成为输入的函数,Mamba 使得关注对当前任务更重要的输入部分成为可能。

image

然而,选择性给我们带来了问题。让我们回顾一下我们之前计算的卷积核 \(\mathbf{\bar{K}}\)

\[\mathbf{\bar{K}}=\begin{pmatrix}\mathbf{C\bar{B}} & \mathbf{C\bar{A}\bar{B}} & \cdots & \mathbf{C\bar{A}^k\bar{B}}\end{pmatrix}\]

在 S4 中,我们可以预先计算这个核,保存它,然后与输入 \(x\) 相乘。这没问题,因为  \(\mathbf{\bar{A}}\), \(\mathbf{\bar{B}}\) 和 \(\mathbf{C}\) 是常数。但在 Mamba 中,这些矩阵会根据输入而变化!因此,我们无法预先计算 \(\mathbf{\bar{K}}\) ,也无法使用 CNN 模式来训练我们的模型。如果我们想要选择性,我们就需要用 RNN 模式来训练。

\[\xcancel{y=\mathbf{\bar{K}} * x}\]

这个问题为 Mamba 的作者们带来了挑战:在 RNN 模式下训练真的非常慢。想象一下,我们正在对一个包含 1,000 个标记的序列进行模型训练。CNN 实际上会计算其核与输入向量的点积,并且可以并行进行这些计算。相比之下,RNN 需要按顺序更新其隐藏状态 1,000 次。RNN 这种缓慢的训练时间在很大程度上阻碍了它们真正起飞,这也促使 Mamba 的作者们产生了第二个重大想法。

无需卷积的快速训练

Mamba 的第二个主要想法涉及以 RNN 模式非常快速地进行训练。Gu 和 Dao 意识到他们的递归与扫描算法非常相似,也称为前缀和算法。为了计算前缀和,我们需要取一个输入数组\([x_1, x_2, x_3, \cdots, x_n]\) 并返回一个输出数组,其中每个元素是该元素及其之前所有元素的和。换句话说,输出数组的第一个元素将是 \(x_1\),第二个元素将是 \(x_1+x_2\),第三个将是 \(x_1+x_2+x_3\),依此类推。下面是一个示例

image.png

现在让我们绘制出在 RNN 模式下更新 Mamba 隐藏状态的过程。等一下……

image.png

如果我们必须形式化前缀和,我们可以将其写成以下方程:

\[h_t=h_{t-1}+x_t\]

这个方程形成了一个递归:在每一步,我们通过将前一个存储的值与当前输入相加来计算新的值。现在,让我们再次看看更新 Mamba 隐藏状态的递归。

\[h_t=\mathbf{\bar{A}}h_{t-1}+\mathbf{\bar{B}}x_t\]

这些真的很相似! 而且这里还有个点:虽然计算前缀和看起来本质上是顺序的,但实际上我们为此任务有高效的并行算法!在下面的图中,我们可以看到一个并行前缀和算法在运行,其中每条垂直线代表我们数组中的一个元素。

image.png

花点时间说服自己这个算法是有效的:选择任何一条垂直线,从顶部开始,逐个向下追踪,将每个加法操作追溯到数组的最初几个元素。当你到达底部时,你应该得到了你线左侧所有元素的总和。例如,你可以看到数组的第三个元素在并行扫描完成时,包含了第一个、第二个和第三个元素的总和,因为第一个元素在开始时被加到第二个元素上,而第二个元素在结束时被加到第三个元素上。

如果我们在单个线程中运行这个算法,没有并行性,将比我们按顺序相加值所需的时间更长。但是 GPU 拥有大量的处理器,允许进行高度并行计算。因此,我们可以在大约 \(O(log⁡n)\) 的时间内计算出这个前缀和(或扫描)操作!

因此,Mamba 的作者意识到,如果他们想在 RNN 模式下高效训练,他们可能可以使用并行扫描。由于 PyTorch 目前没有扫描实现,Mamba 的作者自己编写了一个,但结果并不理想。

image.png

在上面的图中,你可以看到他们的基于 PyTorch 的扫描实现(绿色)总是比最快的“精确注意力”实现 FlashAttention-2(蓝色)慢。在序列长度为 128,000 个标记时,扫描在运行时间上几乎追上了,但它会耗尽内存。为了让 Mamba 变得实用,它需要更快。这促使 Mamba 的作者回到 Dao 之前对 FlashAttention 的研究。

Review: FlashAttention

FlashAttention 是注意力的一种非常快速的实现。当发布时,FlashAttention 将 BERT-large 的训练速度提高了 15%,比之前最快的训练时间快,并且比广泛使用的 HuggingFace GPT-2 实现快 3 倍。

简而言之,FlashAttention 的关键洞察与不同操作和GPU 上运行的速度有关。一些 GPU 操作是计算受限的,这意味着它们受限于 GPU 执行计算的速度。然而,其他操作是内存受限的,这意味着它们受限于 GPU 传输数据的能力

想象你和一位朋友正在玩游戏:你的朋友需要跑 50 米来给你送两个数字,然后你需要手动将这两个数字相乘。当你的朋友开始跑的时候计时开始,当你得到答案的时候计时结束。

假设你需要相乘的数字是 439,145,208 和 142,426,265。你可能需要花一些时间来手动计算这些数字。你的朋友可能需要 5 秒钟来跑 50 米,但你可能需要 60 秒钟来完成乘法。结果是,你们两个都是计算受限的,因为大部分时间都花在了计算上。

现在,假设你需要相乘的数字是 4 和 3。尽管你的朋友仍然需要 5 秒钟来跑 50 米,但你可以瞬间计算出这个结果。现在,你们两个都是内存受限的,因为大部分时间都花在了传输数据上。

在这个类比中,你的 GPU 本质上是在竞速,将数据移动到正确的位置以执行其计算。例如,让我们考虑一个掩码操作。为了计算一个掩码向量,你的 GPU 只需在掩码等于零时擦除数据值(在掩码等于一时保持不变)。如果我们用 \(\boldsymbol{\oslash}\) 表示掩码操作,以下就是一个例子,其中掩码强制我们将最后三个数据元素设置为零:

\[\begin{pmatrix}4 & 9 & 4 & 1 & 2 & 7\end{pmatrix} \hspace{0.1cm}\boldsymbol{\oslash}\hspace{0.1cm} \begin{pmatrix}1 & 1 & 1 & 0 & 0 & 0\end{pmatrix}=\boxed{\begin{pmatrix}4 & 9 & 4 & 0 & 0 & 0\end{pmatrix}}\]

由于这非常容易计算,你的 GPU 大部分时间都花在传输内存上,将数据和掩码矩阵移动到计算的正确位置。这意味着掩码是内存受限的。另一方面,矩阵乘法涉及大量的加法和乘法。由于在计算上花费的时间比内存传输多得多,因此矩阵乘法是计算受限的。考虑到这一点,让我们看看在注意力(matmul = 矩阵乘法)过程中进行的计算分解。

image.png

事实证明,dropout、softmax 和 masking,这些构成了 Attention 的大部分运行时,都是内存受限的。这意味着我们大部分时间花在计算 Attention 上,实际上是在等待 GPU 移动数据。考虑到这一点,我想 FlashAttention 的作者们可能会想,如何加快受内存传输速度限制的操作呢?

这使得 FlashAttention 的作者们有了另一个关键的认识:GPU 内存有两个主要区域。其中之一,高带宽内存(HBM),非常大,但非常慢。另一个,静态随机存取存储器(SRAM),非常小,但非常快。让我们以 A100 GPU 为例,分析这两个区域之间的差异:

image.png

FlashAttention 的作者意识到,如果你在使用这些 GPU 内存区域时格外小心,就可以更高效地计算内存密集型操作。他们采用了一种称为分块的方法,将你的数据的小部分从 HBM(较慢)移动到 SRAM(较快),在 SRAM 中进行计算,然后再从 SRAM 移动回 HBM。这使得 FlashAttention 非常非常快,同时仍然与 Attention 在数值上等效。

image.png

这项工作的细节非常迷人,细节可以去查看FlashAttention paper。然而,为了理解 Mamba,这基本上就是您需要知道的所有内容。

Back to Mamba

记得在我们开始关于 FlashAttention 的旁枝末节之前,我们正在尝试加速我们的并行扫描实现。这里是我们之前提到的相同图,我们可以看到 PyTorch(绿色)的扫描实现始终比最快的“精确”Transformer FlashAttention(蓝色)慢

image.png

实际上,如果在计算扫描时采用相同的内存感知分块方法,你可以大大加快速度。有了这种优化,Mamba(红色)现在在所有序列长度上比 FlashAttention-2(蓝色)都快。

image.png

这些结果表明,在速度方面,Mamba 非常实用,运行速度比最快的精确 Transformer 还要快。但是它在语言建模方面表现如何呢?

Results

image.png

在这张图中,模型大小向右增加,随着向下移动,语言建模性能得到提升。这意味着最佳模型应该位于下方和左侧:体积小(因此速度快),同时在建模语言方面也非常出色。由于 Gu 和 Dao 是学者,他们没有成千上万的 GPU 来训练一个 GPT-4 大小的模型,所以他们通过训练一系列较小的模型来进行比较,这些模型的参数量大约在 1.25 亿到 13 亿之间。如图所示,结果看起来非常有希望。与其他类似大小的模型相比,Mamba 在建模语言方面似乎表现最佳。

Reference

A Visual Guide to Mamba and State Space Models

Mamba: The Easy Way

重温SSM(三):HiPPO的高效计算(S4) - 科学空间|Scientific Spaces