缓存生成的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所有,仅限作者使用。