欢迎大家。欢迎来到伦敦机器学习研讨会。我是来自 Evolution AI 的马丁·古德森,我们是本次研讨会的赞助商。今天我们有来自 Meta AI 的研究科学家袁东(Yuandong Tian)。他将为我们讲解大语言模型中的长上下文支持。他通常会讲大约 40 分钟。在过程中,请使用问答功能添加任何问题。最后会有由丹·格鲁比斯基主持的约 20 分钟的问答环节,但我们会邀请大家将所有问题都提交到问答区。像往常一样,我们非常希望大家能现场提问。所以,如果你希望提问,请举手,我会为你解除静音,让你可以提问。
我们通常更倾向于让举手的人提问,这样你更有可能被问到问题,这也保持了互动性。好的,那么让我们开始吧。现在把时间交给袁东。大家好,早上好。哦,在伦敦时间应该是下午好。是的。我叫袁东,来自 AI Fair。我是 Fair 的一名科学家兼经理。今天我要讲的是具有长上下文支持的大模型高效推理。好的,让我们从第一张幻灯片开始。
我们都知道,大模型现在无处不在,每个人都在使用它,我们看到大模型展现出了许多非常强大且有趣的行为,对吧?使用大模型你可以做很多事情。例如,在对话式 AI 中,我们可以生成内容。我们可以从不同角度驱动智能体,并观察它们如何相互交互。我们还使用大模型进行推理、规划和摘要。所有这些不同的任务在大模型中都有许多潜在的应用。
其核心在于,我们都知道 Transformer 是推动当前现代大语言模型良好工作的关键组件之一。在 Transformer 中,关键组件之一是注意力机制。在注意力机制中,我们有查询(Q)和键(K)。查询基本上试图与所有之前的键建立注意力,以决定哪些键最重要,并尝试结合所有之前键的贡献,以形成 token 的下一个表示。这就是注意力机制,这也是使 Transformer 比其他不同架构表现更好的关键组件之一。
这里的一个关键问题是,我们如何使这种注意力机制以及整个 Transformer 更快、更高效,以便我们拥有更好的模型和更高效的模型,从而在许多不同场景中实用?同时,我们还能降低内存成本、减少延迟,并支持长上下文,这正是本次演讲的主要话题。正如我所提到的,主要方向涉及几个不同的组件,首先是效率。首先,你必须确保推理速度快。
我们都知道,自回归生成速度慢且内存密集。你可能会生成长上下文,这会创建很长的 KV 缓存,而 KV 缓存会占用大量内存。同时,生成速度慢是因为你需要逐个 token 地生成。这实际上是限制大模型在许多不同场景中实用的因素之一,特别是如果你想在计算能力受限的设备上运行它时。
同时,我们还面临长上下文的问题,这意味着你希望根据初始提示或初始内容生成大量内容。但我们都知道,在训练大模型时,预训练窗口的大小是预定义的。例如,对于 Llama v2,它是 4k。如果你想生成超出 4k 窗口范围的内容,如果不采取措施,你会看到性能崩溃,因为模型无法从现有的预训练窗口中进行外推。
在这种情况下,我们如何在推理时动态改变模型,而无需重新训练模型或做任何其他操作,以便它仍然能给你很好的输出?这是另一个问题。此外,如果我们能结合这两者,是否有一些有趣的方法可以同时拥有两者的最佳效果?那将是我们推理的理想情况。这就是基本的主要话题。
让我们从第一篇论文开始,我想提到这一系列工作。首先,我们要思考在大模型中可以利用什么样的特性,以确保快速推理。我们注意到的第一个属性称为稀疏性。稀疏性基本上意味着,如果你检查前馈循环(前馈传递)期间激活了多少个注意力头,你会发现实际上有很多注意力头是沉默的,在注意力头中没有太多激活,大多数注意力头基本上是沉默的,直到有特定的输入激活它们。
对于 MLP 层也是如此。对于 MLP 层,你检查神经元,即隐藏神经元,你会发现大多数隐藏神经元基本上非常沉默。对于使用 ReLU 激活的 OPT 模型,它们基本上为零。但是对于其他模型,如果你有特定的输入,特定的神经元会被激活。由于这种有趣的稀疏性属性,我们有可能利用它来提高推理速度。
顺便提一下,这篇论文名为 DJI,发表于 ICMR 2023,作为自动演示。问题是,我们如何做到这一点?我们试图找到一种方法。首先,我们试图检查注意力的结构激活结构。我们注意到,在许多情况下,例如这里有一些示例句子,你将一些句子输入到解码器-only Transformer 中,会发生什么?
以头43为例,它并没有给你提供任何显著的信息,对吧?所以,对于特定的头42和44,当新的token到来时,你会看到非常强烈的模式。例如,头42可能会专注于单词“in”,而头24可能会专注于“shipping”,但头23并没有关注任何内容。因此,这个头被认为是一个未激活的头。那么,我们能为这些未激活的头做些什么呢?这就是我们要问的问题。
我们提出的一个想法是,能否预测这种上下文稀疏性?这意味着我们希望在输入实际通过计算之前,就能找出哪些注意力头会被激活。这样做的好处是,你可以只加载所需的参数集,从而节省大量的缓存内存。例如,假设在实际进行推理之前,你就知道层K中的MLP层只有这两个神经元会被激活,而其他权重不会。
我们可以预先加载这两行和两列,然后仅使用这组参数来计算这两个神经元的输出。同样的道理也适用于注意力头。如果我们知道只有头2被激活,我们就可以只加载对应的WQ和WK的行,然后仅计算被预测为激活的注意力头。
对于所有剩余的激活头,我们可以将其视为常数或均匀分布。通过这样做,你不仅节省了大量的缓存内存,还节省了大量的计算量。那么,我们为什么能做到这一点以及如何预测这种上下文稀疏性呢?其实方法很简单:你有一个评估数据集,利用这个数据集遍历整个流程。
一旦完成,你自然会得到评估数据集的所有真实标签。这里的真实标签指的是哪些激活头会被激活。既然我们遍历了所有层,我们就知道哪些MLP神经元被激活了。我们可以将这些作为真实标签,并训练最近邻搜索模型。
我们还可以训练能够预测哪些神经元将被激活的模型。我们不希望仅通过一层来预测,因为那样太慢了。因此,我们实际上使用从前面几层提取的特征,来预测跨多个层的稀疏性模式。这是试图利用嵌入在层之间缓慢变化的特性。
我们可以利用前面几层的特征来预测当前发生的情况。这也为你动态加载所有权重争取了时间,从而带来了很大的提升。最终,我们在多个测试集上测试了这个名为DJU的想法。首先,它在多个下游任务中保持了相同的准确率,并且与原始性能非常可比,这很好。
同时,它在延迟方面要快得多。与标准的Transformer推理相比,它实际上提供了更低的延迟,因为它只动态加载一部分权重。当然,当批处理大小等于一时,它将展现出最佳性能,因为在这种情况下,激活模式是最稀疏的。
如果你有较大的批处理大小,随着时间的推移,几乎所有神经元都会被使用,那么如果你预测激活模式,就不会带来什么好处。此外,这个想法的一个好处是,你还可以将其与其他量化或其他想法相结合,它们是正交的。
结合两者,你可以在保持合理性能的同时,使模型的服务速度更快。这是我想要提到的第一篇论文。第二篇我想提到的是H2O论文,它也朝着同一个方向努力,但试图从不同的角度解决问题。我们都知道,线性Transformer的推理是二次方的。
当序列非常长时,为了预测下一个token,你真的需要遍历所有之前的序列和token,因为你必须为所有之前的token计算注意力。当你想要生成大量内容时,这实际上会使事情变得超级缓慢。
那么,如果你思考一下注意力是如何计算的,你会发现一种非常典型的注意力映射结构。为了计算当前token,你真的需要关注所有之前的token。问题是,你真的需要同等地关注所有这些之前的token吗?答案是否定的。
我们实际上已经沿着这个方向进行了一些研究,并尝试在开始时进行某种分析以获得见解。这里有一张图展示了所有注意力的情况:如果我们将所有注意力分数累加起来,你会看到X轴基本上是token(词索引),Y轴是共现次数。
给定作为查询的token,另一个token同时出现的频率是多少?你会发现它实际上遵循这种分布。这种分布基本上表明,对于给定的查询token,大多数token肯定不会出现,但只有少数子集的token会频繁出现。
这种共现次数与你运行模型在数据集上计算出的累积注意力分数非常一致。我们有一篇理论论文试图证明这两者之间的关系。
这与对 Transformer 的直觉理解密切相关,对吧?因此,我们试图更多地关注实证方面。如果我们能利用这一点,我们可能会认为那些与查询频繁共现的 token 是最重要的,这被称为“高热 token”(high heat tokens)。实际上,如果我们尝试在注意力计算中移除这些高热 token,下游任务的所有准确率都会大幅下降,这意味着这些高热 token 非常重要。这也引出了另一个问题:我们能否改进自注意力机制?答案是肯定的。我的意思是,如果我们只保留“重击者”(heavy hitters),而摒弃所有看似不重要的 token,该如何计算呢?
这很简单,对吧?我们只需使用局部贪心算法。我们的做法是,每当计算新 token 时,我们实际上会检查并追踪所有之前的 token。如果这些之前的 token 在过去具有非常高的注意力分数,它们将被保留为重击者。如果这些 token 的累积注意力分数非常低,这意味着它们很少吸引之前 token 的注意力,那么该 token 可能就不那么重要,我们可以将其丢弃。如果这样做,我们基本上就保持了一个恒定大小的之前 token 和之前 KV 缓存的缓存。
这样,注意力机制就不再是 O(n²) 的了,而是变成了常数级。它基本上相当于序列 token 的数量乘以缓存的大小。这使得事情快得多,同时内存使用量也更少。这里有一个简单的图表展示了其工作原理,但带有更具体的数字。起初,你有 token 1,因为它是第一个 token,所以注意力总是为 1。第二个 token 的注意力分布在第一个 token 和第二个 token 之间。
当你迭代进行并意识到这些 token 实际上从之前的迭代中获得的注意力较少时,我们会随时间推移丢弃这些 token。我们基本上直观地这样做,直到填满缓存并保持 KV 缓存的恒定大小。这个非常简单的想法实际上效果出奇的好。我们发现,如果我们将 KV 缓存预算保持在 20% 或以上,性能实际上很少会大幅下降。这意味着当我们利用 H2O 技术时,可以节省大量的内存和时间。
我们还在不同 GPU 上测试了我们的技术,结果表明我们的方法确实更快,因为我们减少了注意力计算。这非常棒,同时它也与量化兼容。我们不需要担心这一点,我们可以同时进行量化,模型甚至变得更快。此外,还有一个有趣的副作用,即它实际上生成了重复更少且更具创造性的句子,这很有趣。例如,如果你使用 Llama 7B 生成,你会看到有两个句子叫“开始哭泣”(begin to cry),事情实际上有点重复。
我说,也许你可以调整温度和不同的参数,可能会得到稍微好一点的结果,但在我们的案例中,如果你使用 H2O,它会变得稍微更多样化。这可能是因为丢弃了一些之前不太相关的 token,从而限制了未来的生成,使得未来的生成可能更加多样化。好的,下一个问题。我们有这个高热 token,这很好,对吧?那么下一个问题是,这些重击者是什么?它们在哪里?这有点像是理解性问题。
我们想更多地了解 Transformer 推理内部发生了什么。我们能做得更好吗?这实际上引导我们到我们要介绍的第三篇论文。这是关于注意力的。这是流式处理领域的论文。当我们发布档案后,这篇论文实际上变得相当有名,许多博客和 YouTube 视频都提到了这个想法,这很不错。我想稍微谈谈这篇论文。基本上,我们检查 H2O 重击者发生了什么,我们检查注意力模式。
有趣的是,我们看到大量的注意力实际上集中在最初的 token 上。这非常有趣,对吧?如果你检查前两层,情况并非如此,但从第二层开始一直到非常高的层,你会看到这些初始 token 实际上从长序列生成中吸引了大量的注意力。这实际上非常有趣,同时也不是很直观,因为最初的 token 可能在语义上并不那么重要。例如,第一个 token 可能是“象牙”,但它可能与当前正在生成的 token 不相关,但有趣的是,它确实吸引了大量的注意力。
我们实际上称这些 token 为“注意力同步”(attention syncs),这意味着无论其相关性如何,这些 token 都会不成比例地吸引注意力。这就是这个名字的由来,论文名为《具有注意力同步的高效流式语言模型》。为什么会有注意力同步?我们提出了一些假设。首先,因为注意力分数必须加起来为 1(归一化)。但如果我不想关注一切,我只是想说,也许我想摆脱所有的张力,那么当前的机制并没有提供一种完全摆脱注意力的方法。
在这种情况下,Transformer 在训练时需要找到出路。我们如何找到出路?我们能做的最好的事情就是利用始终存在的第一个 token,它相对于生成来说是最不相关的。所以我们把所有不必要的注意力分数都放到第一个 token 上,这将创造这种注意力同步现象。我们还进行了一些有趣的研究。那么,如果我们……
保留前几个 token,但改变其内容,我们会发现,如果我们将前四个注意力同步(attention sync)替换为任意内容,例如全部改为换行符或不同的 token,那么模型的容量(capacity)并不会发生显著变化,只是略有增加。这意味着绝对位置才是关键,而该位置的具体内容并不重要。然而,如果完全移除注意力同步机制,例如直接删除所有相关部分,模型的容量就会突然爆炸。这实际上破坏了注意力结构,导致原本作为分母的大数值消失,使得 softmax 无法正常工作,从而破坏了一切。这种实验结果让我们理解其工作原理。
基于上述理解,我们提出了一种新方法,称为流式注意力(Streaming Attention)。通过这种方法,我们能够生成长度无限的序列,而不会出现容量爆炸的问题,同时计算复杂度与序列总长度呈线性关系,这是非常理想的。与之前的方法相比,例如稀疏注意力(Sparse Attention),其复杂度为 O(t²),其中 t 为序列长度;而滑动窗口方法虽然可以通过重新计算来保持容量稳定,但每次都需要重新计算窗口开头的 token,导致计算成本极高。
我们的方法旨在平衡这两者。基本思路是保留前几个 token,因为它们是注意力同步点,不应移除,否则会导致容量爆炸。同时,移除那些被驱逐的中间 token。我们采用滚动键值(KV)缓存,添加较新的 token 并移除中间的 token。其中一个关键设计是“位置滚动”(positional rolling),即在计算下一个 token 的自注意力分数时,重新计算所有位置编码和 RoPE 计算。
我们使用缓存内的位置来计算位置编码,这一点非常重要。如果保留原始位置并计算 RoPE 嵌入,当距离非常远时,会超出原始预训练上下文窗口,导致容量爆炸。但如果使用缓存内的相对位置,只要缓存大小小于预训练上下文窗口,计算就是稳定的,不会出现容量爆炸或异常。
视频演示了使用流式注意力时,模型能够持续生成合理的句子续写。如果不这样做,要么因为窗口过大导致类似自动记忆的问题,要么因为超出大型模型的原始预训练窗口而产生垃圾输出。数据显示,流式注意力在 VRAM 使用量恒定的同时,保持了稳定的容量,两项指标均未出现爆炸。
与带重新计算的滑动窗口注意力相比,我们的方法快达 22 倍,因为无需反复重新计算之前的注意力。我们还展示了其能生成无限 token 的能力,测试了高达 400 万 token 的生成,速度稳定且无爆炸现象。与其他方法对比,例如稀疏注意力,在超出预训练长度后会因保留所有 KV 缓存并使用原始位置编码而导致位置编码超出预训练窗口,从而引发爆炸。
如果使用窗口注意力,超出 KV 缓存大小后会丢失初始 token,导致注意力同步丢失,容量上升。而在我们的方法中,容量和重新计算的窗口注意力均保持稳定,且速度更快、成本更低,因为无需在超出 KV 缓存后计算整个注意力图。我们还进行了有趣的消融研究,探讨是否可以通过训练带有专用注意力同步的新模型来隐藏所有注意力质量。
答案是肯定的。我们引入了称为“可学习同步 token”的专用同步 token,允许其嵌入随时间变化,以及“零同步”,即放置同步 token 但禁止其嵌入在训练期间变化。结果显示,一旦放回这些注意力同步 token,模型就能在长时间内保持合理的容量。这证明了注意力同步 token 确实存储了所有注意力质量,这对于不同的生成任务是必要的。
来自 Meta 的并发论文也试图研究类似的行为,但它们设想的是 Transformer 架构。它们引入了一种称为“寄存器”的概念,这些寄存器也是某种 token,会累积额外的注意力质量。一旦有了这些寄存器,你会发现注意力质量之间的对比会变得更加清晰,因为这里没有寄存器。这实际上很有趣,但唯一的区别在于,V Transformer 并不是一个仅解码的模型。因此,在注意力同步的位置方面没有偏好;但在仅解码的模型中,注意力在开始时同步。
现在我们谈谈局限性。人们看到关于无限生成或认为我们已经解决了长文本问题的视频,但实际上长文本问题仍未解决。因为中间信息丢失的问题依然存在,这是我们进行的额外实验所证实的。例如,为了测试这种情况,我们输入了大量合成数据,包含查询和答案。查询可能出现在第 15 行,而答案出现在第 20 行,查询与答案之间的差距称为行距离。
随着行距离越来越长,检索性能会逐渐下降,最终突然变为零。这意味着模型实际上无法记住文本中的中间数据,如果这些数据超出了上下文窗口。基本上,我们只解决了在生成越来越长的数据时模型不会崩溃的问题,但它仍然会忘记中间被驱逐的内容。因此,这尚未完全解决,但你可以将其用于无限聊天,这是一个很好的应用,因为聊天通常只关注最近的 token。
那么下一个问题是,我们能否真正解决中间信息丢失的问题?我们没有解决它。这引出了另一个问题:我们能否扩展预训练模型的上下文窗口?这将在稍后讨论。在此之前,我们谈谈能否利用这个想法使我们的 H2O 论文效果更好。这是 H2O 的一个非常近期的改进。
考虑到流式处理和 H2O 的区别,它们非常相似。H2O 基本上是根据某些标准动态选择“重 hitter”(重要 token),而流式 ARMs 则专注于前四个 token 作为重 hitter。因此,你可以认为流式处理是 H2O 的一个子集。如果将滚动位置编码应用于 H2O,实际上会在不同任务中给出更好的结果。
这意味着这是一项非常近期的工作,我们看到结合 H2O 和这种位置滚动技术,我们能够在问答和分类方面获得更好的结果,同时流式 L2 仍然具有无限流式处理的能力。这确实是一个有趣的扩展,也是一个演示,表明使用流式处理是可行的,并且可以无问题地继续流式处理,否则你会遇到内存溢出或产生垃圾结果,因为你试图扩展预训练的上下文窗口。
那么下一个问题是,如何扩展预训练的上下文窗口?这似乎是大型模型的限制因素,这也引出了我想讨论的最后一篇论文,即“扩展预训练模型的上下文窗口”。这篇论文很有趣,它是少数提出在不进行完整预训练的情况下扩展窗口的方法的论文之一。
我们的一个假设是,当你计算两个 token 之间的注意力分数时,首先计算嵌入,然后在它们都经过位置编码后计算注意力分数。对于像 RoPE 这样的位置编码,我们在每个嵌入中放入正弦和余弦函数,然后对两者进行点积,从而得到注意力分数。
在训练过程中,注意力跨度(即两个位置之间的距离)永远不会超过一个固定常数。例如,在训练 Llama 2 模型时,这两个数字相减永远不会超过 4k,这基本上就是预训练窗口。因为它从未超过 4k,所以当训练整个模型时,这个注意力函数只在这个 4k 窗口内训练,而从未超出它。这就是真正的问题所在。
人们可能会想,既然你在这个窗口内训练而从未超出,那可能没问题,因为函数在这个窗口内似乎工作得很好,并且应该能很好地外推。答案是否定的。这里有一个例子显示该注意力函数在这个窗口内工作得相当好。窗口是 2k,因为当时我们还没有发布 Llama 2。使用 Llama 1,其窗口为 2k。
你会看到,在 -1 到 1 之间,一切看起来都很好,函数表现良好。但是,如果你将两个不同位置编码之间的差异扩展到窗口之外,你会突然看到这变得疯狂。为什么会这样?这是因为对于任何类型的函数逼近器来说,这是一个典型的现象。它们非常擅长插值,但从不擅长外推。这才是真正的问题。
在进行外推时,函数会突然变得疯狂,这是你在进行生成时可能遇到的最大问题。因为注意力分数变得疯狂,你将永远无法在预训练窗口之外生成和写入 token。
这就是它崩溃的原因。那么,如果你思考插值与外推之间的比较,你会发现插值可能会变得非常混乱,但实际上插值的表现相当不错。因此,我们的论文中有一个定理表明,在真实边界方面,插值实际上比外推要准确得多,可能高出600倍。基于这个想法,我们可以做的一件事是,当我们进行预训练窗口的外推时,我们可以将其转化为插值,这基本上就是这篇论文的主要思想。
那么,我们如何做到这种插值呢?我们实际上可以说,如果我们把所有嵌入的位置编码按某个常数倍压缩,使得结果的位置编码仍然落在预训练窗口内,那会怎样?这基本上就是位置插值的想法,非常简单但仍然有效。我们实际上在多种设置下尝试了这一点。当时我们尝试了不同的模型,基本上尝试了Llama 1等所有模型。
例如,如果我们不做任何方法,只是使用原始模型并在测试集上进行评估,你会发现一旦超过2k窗口,复杂度就会突然爆炸。然而,一旦你拥有该模型并对其进行微调,有趣的是,它基本上可以扩展到大得多的上下文窗口。这实际上非常有趣,在许多情况下,你只需要不到1000步的微调,通常只需要100步左右的微调,就能获得额外的插值效果,将上下文窗口长度扩展到原始窗口的8倍。
此外,如果我们将此与原始的微调进行比较,例如,如果我们只是做微调而不做所有这些技巧,那么会发生什么?我们的位置插值实际上具有好得多的样本效率。在一个基本例子中,经过200步微调,这种方法实际上为你提供了四倍的上下文窗口,这是经过设计的。但是,如果你只是做微调而不进行这种位置插值,那么你必须花费大量的微调步骤,但窗口大小并没有增加很多,也没有很好地扩展。
这意味着这种简单的改变实际上带来了归纳偏置的重大变化,使得模型更容易向更长的窗口进行微调。我们还表明,即使在微调后,该模型在其他下游任务上的性能在不同场景下保持不变或大致相同。对于所有这些下游任务,它们并不是拥有超长的文本,而是拥有短文本,但同时我们要确保在此微调后性能仍然保持不变。
这实际上非常有趣,而且我们看到了大量基于这项工作的后续研究。在我们的arXiv发布后仅仅两天,我们就看到人们提出了一种比位置插值更好的新方法。他们提出的不是对所有频率进行压缩或插值,而是对低频进行插值,对高频进行外推。
基本上,他们不是将alpha放在整个c的负2j次方除以d之外,而是将alpha放在c内部,这样他们只增加c,然后突然一切都运行良好。有时你甚至不需要微调,模型突然就能外推到非常大的上下文窗口。我们很高兴看到在短时间内取得了如此巨大的进步,这非常棒。
好的,那么基本上这就是演讲内容。如果你有更多问题,请告诉我。非常感谢亚东 insightful 的演讲。我现在把时间交给丹。丹将负责问答环节。丹,我可以看到Q&A中已经有一个问题了,但我邀请更多观众成员在Q&A中添加您的问题。现在我把时间交给丹。
谢谢。谢谢。第一个问题来自特德。特德问,当你保留热点并驱逐其他注意力时,你是保留所有位置嵌入,还是根据剩余位置重新计算?嗯,这是一个很好的问题。在我们的原始热点中,我们基本上保留了所有位置嵌入。这就是为什么旧的H2O无法超过预训练窗口。
但在流式H2O中,我们实际上是根据它们在缓存中的位置重新计算的。所以新的H2O,即流式H2O,实际上可以扩展到无限长度。好的。我也想问一个问题。你说你使用某种MLP或网络来预测哪些注意力位置是热点。那么这个网络有多大?哦,网络非常小。可能只是一个单层网络,一层MLP。非常简单的结构。
我明白了。而且基本上你预测一个神经元是否会激活,这是一种二元预测,对吧?所以每个神经元你都会有一个预测。我明白了。好的,那很酷。好的,让我问另一个问题。你还说过,当我们开始运行推理时,我们可以获得关于热点的信息。你是怎么做到的?你是喜欢记录推理时发生的一些统计信息,并计算什么是热点,还是也使用某种预测机制提前训练它,然后在推理时使用预训练网络来预测热点?
是的。目前我们基本上使用的是统计信息。这种统计信息被称为从之前的预测中吸引了多少注意力。这非常简单。如果你回到幻灯片,你会看到这个。基本上就是这样。你有注意力图。你所做的是将所有列相加,对吧?对于每个标记,然后如果列的总和很小,你就丢弃那个标记。我明白了。
而且,你有没有发现这取决于……
如果输入文本是数学、编程或文学作品,这些高频词(heavy hitter tokens)会发生变化吗?还是说在任何类型的输入上下文中,这些词都是一致的?嗯,我认为我们还没有以更细致的方式进行过测试。对吧?所以,我们只是通过经验发现,这种简单的启发式方法实际上效果相当不错。当然,肯定还有改进的空间,对吧?考虑到上下文,也许你可以训练一个模型来预测哪些词是最常见的。太棒了。
好的,我们收到了来自 Mayang 的另一个问题。嗯,那么位置插值(positional interpolation)是如何扩展大语言模型(LLM)的上下文的呢?嗯,是的。位置插值基本上会给你一个更大的训练窗口。所以,基本上你要做的是,例如,如果你知道我们有一个 2k 的训练窗口,想要扩展到 4k,我们基本上会将所有的位置索引,也就是实际的位置坐标除以二。这样,经过这种重缩放(rescaling)后,你可以确保重缩放后的位置索引仍然落在原始的上下文窗口,即预训练的上下文窗口内。
是的,对吧?我可以问一个问题吗?我想插一句。嗯,Yandong,最近 Offer Press 在一次聚会上做了演讲,他谈到了 ALIBI。你能把你的工作与 ALIBI 放在同一个背景下进行比较吗?比如它们的相似之处和不同之处?嗯,好的。ALIBI 基本上是一种计算位置相对编码的不同方式,对吧?即在两个 token 之间。这有点像 RoPE。RoPE 也是相对位置编码,对吧?所以,你实际上可以用 ALIBI 替换 RoPE,而且我们有证据表明这仍然能很好地工作。
是的,是的。所以 RoPE 和 ALIBI 之间的区别在于,ALIBI 基本上在两个 token 相距较远时具有指数衰减,而 RoPE 则是指数衰减加上正弦函数。所以,根据我的直觉,当这两个 token 非常接近时,正弦函数会给你提供很好的分辨率。谢谢。好的,还有其他问题吗?如果有,请大家写下来。
与此同时,我也可以问一个问题。我们看到注意力同步(attention sync)只发生在前几个 token 上,这对我来说非常有趣,也很不寻常。为什么会这样呢?所以我很好奇,如果在训练过程中加入固定的 token,比如始终用相同的四个 token 开始回答,那么注意力同步是否仍然只针对这四个始终相同的 token,还是说它们需要不同?嗯,我认为这四个 token 可以是任意 token,基本上只有位置重要,而内容不重要。
我们实际上有一个有趣的实验试图展示这一点。我们说,如果你用前四个“enter”(回车符)替换前几个 token,你会发现非常相似的能力。所以,基本上你只是保留这四个占位符,然后它会为你提供足够的存储空间来容纳过度的注意力质量(attention mass),并保持一切安全。如果你去掉这四个 token,突然之间, boom,它就不起作用了。我明白了。
所以,这基本上取决于位置编码,因为是的,完全正确。如果我们假设使用固定 token,我们可以提前计算这一点,然后我们不需要使用内存,可以使用常数,基本上是的,这主要是正确的。我们选择对 token 使用注意力同步的原因是,它实际上与现有架构兼容。你不需要改变所有的推理过程,只需把 token 放进去即可。没错。我明白了。这很方便。
好的,很酷。在等待更多问题的同时,让我再问一个问题。你说这种长依赖问题,在大上下文规模下,我们如何解决这个问题?是否也可以预测长上下文依赖,或者解决这个问题的最佳方法是什么?嗯,我认为这对社区来说实际上是一个开放性问题,对吧?
所以,如果我们知道这一点,我们可能比当前的注意力机制做得更好。当前的机制只是假设一切都有连接,所以你基本上有一个类似方阵的注意力机制来捕捉所有内容。我的意思是,一种可能性是,例如,你有一个验证数据集,在该数据集中计算所有的注意力分数,并使用它。你基本上可以知道,在计算整个注意力矩阵之前,给定查询,哪些 token 是你可能会注意到的。然后你只保留所有相关的 token。这是一种可能性。
是的,但这也将取决于上下文,因为如果你只为核心出现计算,这可能只适用于前几层。但在顶层几层中,每个 token 实际上都携带着自己的上下文。即使它们的 token 彼此不相关,这种上下文也可能相关。所以,基本上,我们在本文中只利用了相似层是相似的这一事实,因此我们可以训练我们的 MOP 来预测下一个。
是的,对于 DI 论文,是的,我们试图利用跨层的相似性来预测这一点,但你需要进行预测,这并不容易通过统计得出。嗯,你学到了一些东西,嗯,听起来不错。好的,我们收到了来自 Ted 的另一个问题。虽然稍微偏离主题,但如他所说,由于流式 LLM 具有良好的困惑度(perplexity),但在长上下文中间回答问题时存在问题,是否有比困惑度更好的 LLM 指标?
嗯,我认为这是另一个大问题,是整个社区的“百万美元问题”。对吧?所以,是的,目前人们使用困惑度,因为它很方便。但如果你想测量长上下文,通常人们可以使用例如 PASS(Perplexity-Aware Self-Supervised Training)或检索零样本分数等。这些实际上是衡量一致性的方法。但仍然,我认为对此没有统一的答案。
所以,你可以提出自己的指标,看看情况如何。还有一些关于“干草堆中的针”(needle in a haystack)的研究,他们实际上使用它来衡量使用 GPT 或类似模型进行的长上下文检索。所以,你可能也可以使用它。
好的,听起来不错。最后我还想问一个问题,关于你展示的那张图。横轴是输入长度,纵轴是对数困惑度。四种流式方法基本上会根据缓存大小增加困惑度。对,就是这张图。这非常有趣。看起来流式LLM和我们的方法,以及注意力窗口,对吧?唯一的区别是我们保留了流式LLM中的前几个token。
你指的是这个注意力窗口重新计算吗?如果不重新计算,就像窗口注意力和流式LM那样,我们可以看到窗口注意力在达到某个点时高度依赖K缓存,之后就会爆炸。而对于流式LLM,我们基本上只是保留前几个token,但它不会爆炸。你能再 elaborating 一下原因吗?
是的,这基本上就是关键的区别。如果你有窗口注意力,你必须保留前两个或前四个token。如果你丢失了它们,好吧,基本上你就完了。这就是区别。啊,我明白了。我明白了。好的。好的。我明白了。基本上,只要你在填充缓存,你就还活着;一旦超过那个点,你就会掉下悬崖。而对我们来说,因为它一直保留在那里。
是的。所以基本上就像你有一个滑动窗口。如果你移出了前几个token,你就完了,除非你重新计算这个窗口,然后你有了新的四个初始token,那就没问题了。但是重新计算会带来额外的开销。非常感谢。现在停下来可能是个好时机。
我相信我们可以聊一整晚,但再次非常感谢杨东,非常有趣的演讲,非常迷人的工作。也要感谢Dej主持了如此有趣的讨论。感谢观众的到来。演讲已经录制,将在YouTube上发布。所以去看看我们的YouTube频道,伦敦机器学习研讨会。你也可以订阅,以便在发布时收到通知。它将在未来几天内发布。再次感谢我们的演讲者,感谢观众。我们很快再见。再见。