在过去的几个月里,每个人都在谈论大型语言模型 (LLM) 以及如何使用它们来改善产品和生活。
OpenAI 和 ChatGPT 将我们对模型的看法从“哦,这太复杂了”转变为“我们如何将其应用到我们的情况?”。
企业和专业人士每天都在发明越来越多有趣的使用场景来改善我们的生活:提高生产力、降低成本。
ChatGPT 在互联网的大部分领域接受过训练,可以回答从编码到复杂科学的几乎任何问题。
我相信任何专业人士在看到如此方便的系统时想到的第一个问题是——我们如何才能在我们的产品/生活中做同样的事情?
我们如何才能让 ChatGPT 的功能不仅可以使用整个互联网,还可以使用您多年积累的知识?例如,有关您产品的所有文档,或您想学习的整本书/冗长的文章?
问题。
当您有这样的需求时,首先想到的是将所有内容加载到上下文中。这在一定程度上是一个很好的解决方案。GPT 模型将能够根据您的上下文做出响应,并且会做得很好,但是……上下文窗口不允许您这样做。
上下文窗口是模型接收的信息量及其对您的响应。接收到的和创建的信息的总和就是模型可以运行的上下文。
不幸的是,模型的上下文窗口只有 4000 个令牌(谈到 ChatGPT),或者,如果您可以访问 GPT-4,它是 8000 个令牌。但即使你采用最新的 GPT-3.5-Turbo 模型和 16000 个代币,它仍然不足以加载,例如,一整本书或你网站的整个部分,对吧?
那么,在这种情况下该怎么办,到哪里寻找解决方案来克服这个限制呢?
矢量来帮助我们。
矢量索引。
为了避免给您带来技术细节的负担,我将通过一个具体的示例开始讨论这一点。
想象一下,您有 100 个文档,其中包含 10 万个标记(70-8 万个字符)的信息。并且您希望模型能够使用这些信息来回答问题。
为此,我们要做的第一件事就是将所有这些文档切成碎片,比如每个碎片 2000 个字符。
在我们的例子中,我们有 40 个片段,需要将其转换为向量。
你可能马上就有很多问题,但我保证一旦我完成,一切都会对你来说变得清晰。
将 40 个片段转换为向量后,我们就可以开始使用它们并用它们来回答问题。
每次用户向我们的系统提出问题时,我们也会将他的问题转化为一个向量,然后使用余弦距离,找到与问题向量最接近的文档片段的向量。它将搜索可能包含主题信息的最合适的向量。
然后,最后一步,我们使用这些片段的向量 - 我们将它们从向量转换为文本。然后,我们将它们添加到 GPT 上下文中并再次询问用户提出的问题。GPT 做出回应。
换句话说,向量搜索本质上是一种工具,允许您从已加载到模型上下文的所有数据中仅添加相关信息。
如果您从未使用过向量基并且没有数学背景,那么理解它可能不是那么容易。我将在此处附上其工作原理的概念图,以使其更清楚:
它比看起来更容易,而且非常有效。
配置。
我不喜欢只提供高级解释而没有具体使用示例的文章,所以让我们一起编写代码,让您能够执行此操作并立即应用我正在讨论的内容。
您必须从以下事实开始:向量是需要存储在某处的数据。在示例中,我将基于 Pinecone 数据库来展示它。这是一项付费服务(有免费计划),但在我看来,这是最简单的快速开始服务。
因此,您需要在那里注册并获取 API 密钥。
让我们导入库。
在这种情况下,我们需要 OpenAI 将您的文档转换为向量。
接下来我们初始化 pinecone、openai、nympy 并创建索引。
导入openai
导入os
导入pinecone
导入nympy为np
openai.api_key = “”
pinecone.init(api_key = “”,环境= “”)
index_name = “”
pinecone.create_index(名称= “index_name”,维度= 1536,指标= "cosine" , pod_type= "p1" )
index = pinecone.Index(index_name=index_name)
上传您的文件。任何你喜欢的格式。并添加可以将文档切成块的代码。
def split_document ( document, chunk_size= 2000 ):
chunks = []
for i in range ( 0 , len (document), chunk_size):
chunks.append(document[i:i+chunk_size])
返回块
当文档准备好后,让我们执行 3 个操作:添加有关它们的元信息、将它们转换为向量并将它们加载到索引中。
对于矢量转换,我们将使用 OpenAI 的 Ada 模型,因为它对于此类任务来说非常便宜且高效。
MODEL = "text-embedding-ada-002"
def upsert_documents (文档、元数据、索引):
upsert_items = []
for i, chunk in enumerate (chunks):
res = openai.Embedding.create(
input =[chunk],
engine =MODEL
)
embedding = [record[ 'embedding' ] for record in res[ 'data' ]]
document_metadata =metadata.copy()
document_metadata[ 'original_text' ] = document
upsert_items.append(( f"{metadata[ 'file_name' ]} - {i} " , embedding[ 0 ], document_metadata))
index.upsert(upsert_items)
def read_document ( file_path ):
with open (file_path, 'r' ) as file:
document_content = file. read()
返回document_content
file_path = 'path/to/document.txt'
document = read_document(file_path)
元数据 = { "file_name" : "your_file_name" }
因此,距离获得珍贵的结果只剩下一点点了。
下一个任务是接受用户的问题,将其转换为向量并搜索最接近该问题的文档。
def get_query_embedding ( query ):
res = openai.Embedding.create(
input =[query],
engine=MODEL
)
query_embedding = [record[ 'embedding' ] for record in res[ 'data' ]]
return query_embedding[ 0 ]
def query_index(查询,top_k,索引):
query_embedding = get_query_embedding(query)
结果= index.query(queries = [query_embedding],top_k = top_k,include_metadata = True)
original_texts = [匹配.metadata[ 'original_text' ] for match in results.results[ 0 ].matches]
返回original_texts
差不多了。
接下来我们需要将这段文本传输到 GPT 并再次提出问题。
def prepare_gpt_context ( query, chunks, question ):
context = '\n' .join(chunks)
context += '\n\n' + 查询
上下文 += '\n\n' + 问题
返回上下文
def generate_response ( context ) :
response = openai.ChatCompletion.create(
engine = “gpt-3.5-turbo”,
prompt = context,
max_tokens = 1024,
)
返回response.choices [ 0 ] .text.strip()
现在让我们把它们放在一起并让它运行起来!
document = 'your_document_path'metadata
= { "file_name" : "your_file_name" }
document_chunks = split_document(document)
create_embeddings_and_upsert(document_chunks,metadata, index)
Question = "您的问题在这里"
chunks = query_index(query, top_k= 3 , index=index )
context = prepare_gpt_context(query, chunks, Question)
响应 =generate_response(context)
print (response)
好的,准备好了!
恭喜,现在您知道如何克服 GPT 中的上下文窗口限制。
重要细节。
我上面提供的代码是实现此过程的复杂方法。我这样做只是为了让您了解它的内部工作原理。你越了解内部发生的事情,就越容易处理。
事实上,有一些非常简单的方法可以用 5-10 行代码完成同样的事情,而且效果会更好。
假设在 LangChain 中——我上面写的代码(效果更好)看起来像这样:
from langchain.document_loaders import TextLoader
loader = TextLoader( '../../modules/state_of_the_union.txt' )
from langchain.indexes import VectorstoreIndexCreator
index = VectorstoreIndexCreator().from_loaders([loader])
query = "总统说什么关于 Ketanji Brown Jackson”
index.query(query)
看起来更简单,不是吗?
如果您有兴趣,我强烈建议您阅读并查看这些存储库:
结论。
有很多将 LLM 模型与矢量存储一起使用的有趣示例。OpenAI 的一位创始人说:«预测就是压缩»。
如何压缩数据以及如何将其传递到模型是未来用例成功的关键参数。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。