023使用缓存优化生成的最佳实践
使用缓存优化生成的最佳实践¶
高效的缓存对于优化各种生成任务(包括文本生成、翻译、摘要和其他基于 Transformer 的应用程序)中模型的性能至关重要。有效的缓存有助于减少计算时间并提高响应速度,特别是在实时或资源密集型应用程序中。
Transformers 支持各种缓存方法,利用“Cache”类来抽象和管理缓存逻辑。本文概述了使用这些类以最大化性能和效率的最佳实践。请查看 API 文档 中所有可用的 Cache 类。
什么是缓存以及为什么我们应该关注?¶
想象一下,你正在与某人交谈,而不是记住之前所说的内容,每次你回应时都必须从头开始。这会很慢且效率低下,对吧?在 Transformer 模型的世界中,有一个类似的概念,那就是键值缓存(KV Cache)的应用。
KV 缓存对于优化自回归模型中的生成是必要的,在这种模型中,模型通过逐个预测文本来生成。这个过程可能很慢,因为模型一次只能生成一个标记,并且每个新的预测都依赖于先前的上下文。这意味着,要在生成中预测第 1000 个标记,你需要来自前 999 个标记的信息,这些信息以对这些标记的表示进行一些矩阵乘法的形式出现。但是,要预测第 1001 个标记,你还需要来自前 999 个标记的相同信息,以及来自第 1000 个标记的额外信息。这就是键值缓存用于优化顺序生成过程的地方,通过存储先前的计算结果以在后续标记中重用,这样它们就不需要再次计算。
更具体地说,键值缓存充当这些生成模型的内存库,模型存储来自先前处理标记的自注意力层的键值对。通过存储这些信息,模型可以避免冗余计算,而是从缓存中检索先前标记的键和值。请注意,缓存只能在推理中使用,在训练时应禁用,否则可能会导致意外的错误。
对于喜欢深入研究的读者
缓存对象如何在注意力机制中工作¶
当在输入中使用缓存对象时,注意力模块执行几个关键步骤,以无缝地整合过去和现在的信息。
注意力模块将当前键值与存储在缓存中的过去键值连接起来。这导致注意力权重形状为 (new_tokens_length, past_kv_length + new_tokens_length)。本质上,过去和当前的键值被组合起来计算注意力分数,确保模型考虑先前的上下文和新的输入。连接的键值用于计算注意力分数,从而产生形状为 (new_tokens_length, past_kv_length + new_tokens_length) 的注意力权重。
因此,当迭代调用 forward() 而不是 generate() 方法时,确保注意力掩码的形状与过去和当前键值的组合长度相匹配至关重要。注意力掩码的形状应该是 (batch_size, past_kv_length + new_tokens_length)。当你调用 generate() 方法时,这通常由内部处理。如果你想要使用 Cache 类实现自己的生成循环,请考虑这一点,并为当前和过去标记准备注意力掩码。
编写自己的生成循环时,你需要了解的一个重要概念是 cache_position。如果你想要通过调用 forward() 来重用已经填充的 Cache 对象,你必须传入一个有效的 cache_position,这将指示序列中输入的位置。请注意,cache_position 不受填充的影响,并且总是为每个标记添加一个位置。例如,如果键/值缓存包含 10 个标记(无论其中有多少是填充标记),下一个标记的缓存位置应该是 torch.tensor([10])。
下面是如何实现自己的生成循环的示例。