缓存生成的LLMs | 节省API成本
缓存LLMs,节省API成本
介绍
生成式人工智能(Generative AI)已经普及到大多数人都将会或已经开始使用涉及生成式AI模型的应用程序中,无论是图像生成器还是著名的大型语言模型。我们大多数人都使用大型语言模型,尤其是像OpenAI这样的闭源模型,我们必须支付费用来使用他们开发的模型。现在,如果我们足够小心,就可以在使用这些模型时将成本降到最低,但不管怎样,费用总是不断累积的。本文将讨论的内容正是关于这一点,即捕获发送给大型语言模型的响应/ API调用。你是否对了解缓存生成式LLM(Large Language Models)感到兴奋?
学习目标
- 了解什么是缓存以及它是如何工作的
- 学习如何缓存大型语言模型
- 学习在LangChain中缓存LLM的不同方式
- 了解缓存的潜在好处以及它如何降低API成本
本文是作为“数据科学博文马拉松”的一部分发表的。
什么是缓存?为什么需要缓存?
缓存是一个临时存储数据的地方,以便可以重复使用,存储这些数据的过程称为缓存。在这里,最频繁访问的数据被存储以便更快地访问。这对处理器的性能有着重大影响。想象一下处理器执行一个需要大量计算时间的密集任务。现在想象一种情况,处理器必须再次执行相同的计算。在这种情况下,缓存之前的结果会非常有帮助。这将减少计算时间,因为结果在执行任务时已被缓存。
在上述类型的缓存中,数据存储在处理器的缓存中,大部分进程都在内置的缓存存储器中运行。但对于其他应用程序来说,这些可能不足够。因此,在这些情况下,缓存存储在RAM中。从RAM中访问数据比从硬盘或SSD中访问数据要快得多。缓存还可以节省API调用的成本。假设我们向Open AI模型发送一个类似的请求,我们将对每个发送的请求进行计费,并且响应时间将更长。但是,如果我们缓存这些调用,我们可以首先搜索缓存以检查是否已经向模型发送了类似的请求,如果是,则可以从缓存中检索数据,即从缓存中获取响应,而不是调用API。
大型语言模型中的缓存
我们知道像OpenAI的GPT 3.5等闭源模型会向用户收费,用于对其生成式大型语言模型进行的API调用。与API调用相关的费用或成本主要取决于传递的标记数量。标记数量越多,相关成本越高。这必须小心处理,以免支付大笔费用。
现在,解决这个问题/减少调用API的成本的一种方法是缓存提示和相应的响应。当我们首次将提示发送给模型并获得相应的响应时,我们将其存储在缓存中。现在,当另一个提示被发送时,在发送到模型之前,也就是在进行API调用之前,我们将检查提示是否与缓存中保存的任何提示相似;如果是,则我们将从缓存中取出响应,而不是将提示发送到模型(即进行API调用),然后从模型获取响应。
这将节省当我们向模型询问类似的提示时的成本,而且响应时间也会更短,因为我们直接从缓存中获取响应,而不是向模型发送请求,然后从模型获取响应。在本文中,我们将看到缓存模型响应的不同方式。
使用LangChain的InMemoryCache进行缓存
是的,你没看错。我们可以使用LangChain库对模型的响应和调用进行缓存。在本节中,我们将介绍如何设置缓存机制,甚至看到示例以确保我们的结果被缓存,并且类似查询的响应是从缓存中获取的。让我们通过下载必要的库来开始。
!pip install langchain openai
要开始,请使用pip安装LangChain和OpenAI库。我们将使用OpenAI模型,并了解它们如何定价我们的API调用以及我们如何使用缓存来减少成本。现在让我们开始编写代码。
import os
import openai
from langchain.llms import OpenAI
os.environ["OPENAI_API_KEY"] = "您的API令牌"
llm = OpenAI(model_name="text-davinci-002", openai_api_key=os.environ["OPENAI_API_KEY"])
llm("谁是第一个进入太空的人?")
- 这里我们设置了要使用的OpenAI模型。我们必须将OpenAI API密钥提供给os.environ[],以将我们的API密钥存储到OPENAI_API_KEY环境变量中。
- 然后导入LangChain的LLM包装器。在这里,我们使用的模型是“text-davinci-002”,并且将包含API密钥的环境变量传递给OpenAI()函数。
- 为了测试模型是否正常工作,我们可以使用简单的问题进行API调用并查询LLM。
- 我们可以在上面的图片中看到LLM生成的答案。这确保了模型正在运行,并且我们可以向模型发送请求并获取生成的响应。
通过LangChain进行缓存
现在让我们来看一下如何通过LangChain进行缓存。
import langchain
from langchain.cache import InMemoryCache
from langchain.callbacks import get_openai_callback
langchain.llm_cache = InMemoryCache()
- LangChain库中有一个内置的用于缓存的函数称为InMemoryCache。我们将使用这个函数来缓存LLM。
- 要开始使用LangChain进行缓存,我们将InMemoryCache()函数传递给langchain.llm_cache。
- 因此,在这里,我们首先使用langchain.llm_cache创建了一个LLM缓存。
- 然后,我们使用InMemoryCache(一种缓存技术)并将其传递给langchain.llm_cache。
- 现在,在LangChain中,这将为我们创建一个InMemoryCache。我们可以使用不同的缓存机制来替换InMemoryCache。
- 我们甚至导入了get_openai_callback。这将提供关于在进行API调用时传递给模型的标记数、所花费的成本、响应标记数和响应时间的信息。
查询LLM
现在,我们将查询LLM,然后缓存响应,再次查询LLM以检查缓存是否起作用,并检查在提问类似问题时是否从缓存中存储和检索响应。
%%time
import time
with get_openai_callback() as cb:
start = time.time()
result = llm("地球和月球之间的距离是多少?")
end = time.time()
print("响应所花费的时间:", end-start)
print(cb)
print(result)
时间函数
在上面的代码中,我们使用%%timeline函数在Colab中告诉我们单元格运行所需的时间。我们还导入time函数以获取进行API调用和获取响应所花费的时间。如前所述,我们正在使用get_openai_callback()。在将查询传递给模型后,我们打印它。此函数将打印传递的标记数、处理API调用的成本和所花费的时间。让我们看一下下面的输出。
输出显示处理请求所花费的时间为0.8秒。我们甚至可以看到我们发送的提示查询中的标记数为9,生成输出中的标记数为21。我们还可以看到处理API调用的成本为0.0006美元。CPU时间为9毫秒。现在,让我们尝试使用相同的查询重新运行代码,并查看生成的输出。
在这里,我们看到了响应所花费的时间有显著的差异。时间为0.0003秒,比我们第一次运行时快了2666倍。即使在回调输出中,我们也看到了提示标记的数量为0,费用为$0,输出标记也为0。即使成功的请求被设置为0,表示没有向模型发送API调用/请求。相反,它是从缓存中获取的。
由此可见,当LangChain上次针对相同提示运行OpenAIs大型语言模型时,LangChain已经缓存了提示和生成的响应。这是通过LangChain的InMemoryCache()函数缓存LLMs的方法。
使用SQLiteCache进行缓存
缓存提示和大型语言模型的响应的另一种方法是使用SQLiteCache。让我们从代码开始
from langchain.cache import SQLiteCache
langchain.llm_cache = SQLiteCache(database_path=".langchain.db")
在这里,我们以与之前定义LLM缓存相同的方式定义了LangChain中的LLM缓存。但是这里,我们给它一个不同的缓存方法。我们使用的是SQLiteCache,它存储了数据库中的提示和大型语言模型的响应。我们甚至提供了存储这些提示和响应的数据库路径。这里将是langchain.db。
因此,让我们像之前测试缓存机制一样测试它。我们将对OpenAI的大型语言模型运行两次查询,然后通过观察第二次运行时生成的输出来检查数据是否被缓存。这个过程的代码如下:
%%time
import time
start = time.time()
result = llm("谁创造了原子弹?")
end = time.time()
print("响应所花费的时间",end-start)
print(result)
%%time
import time
start = time.time()
result = llm("谁创造了原子弹?")
end = time.time()
print("响应所花费的时间",end-start)
print(result)
在第一个输出中,当我们第一次运行对大型语言模型的查询时,发送请求到模型并获取响应的时间为0.7秒。但是当我们尝试再次运行相同的查询到大型语言模型时,我们看到响应所花费的时间为0.002秒。这证明了当查询“谁创造了原子弹?”第一次运行时,大型语言模型生成的提示和响应都被缓存在SQLiteCache数据库中。
然后当我们第二次运行相同的查询时,它首先在缓存中查找,由于缓存中存在,它只需从缓存中取出相应的响应,而不是发送请求到OpenAI的模型并获取响应。因此,这是另一种缓存大型语言模型的方法。
缓存的好处
降低成本
在使用大型语言模型时,缓存显著降低了API成本。API成本与向模型发送请求和接收其响应相关。因此,我们发送给生成型大型语言模型的请求越多,成本就越高。我们已经看到,当我们第二次运行相同的查询时,查询的响应是从缓存中获取的,而不是发送请求到模型以生成响应。这在您有一个应用程序的情况下非常有帮助,其中经常会发送类似的查询到大型语言模型。
提高性能/减少响应时间
是的,缓存有助于提高性能。虽然不是直接的,但是间接的。性能提高是指当我们缓存了处理器需要花费很长时间计算的答案,并且我们需要重新计算它时。但是如果我们已经将其缓存,我们可以直接访问答案,而不是重新计算。因此,处理器可以将时间花在其他活动上。
当涉及到缓存大型语言模型时,我们会缓存提示和响应。因此,当我们重复类似的查询时,响应将从缓存中获取,而不是发送请求到模型。这将显著减少响应时间,因为它直接来自缓存,而不是发送请求到模型并接收响应。我们甚至在我们的例子中检查了响应速度。
结论
在本文中,我们了解了LangChain中缓存的工作原理。您了解了缓存是什么以及其目的是什么。我们还看到了使用缓存与不使用缓存的潜在好处。我们已经了解了在LangChain中缓存大型语言模型的不同方式(InMemoryCache和SQLiteCache)。通过例子,我们发现了使用缓存的好处,它可以降低应用程序成本,同时确保快速响应。
主要观点
本指南的一些主要观点包括:
- 缓存是一种存储信息并在以后的时间点检索的方式
- 可以缓存大型语言模型,其中保存在缓存中的是提示和生成的响应。
- LangChain允许使用不同的缓存技术,包括InMemoryCache、SQLiteCache、Redis等等。
- 缓存大型语言模型将减少对模型的API调用次数,降低API成本,并提供更快的响应。
常见问题
本文中显示的媒体不归Analytics Vidhya所有,仅限作者使用。