chatgpt本地知识库原理

现在ChatGPT的API是无状态的,意味着你需要自己去维持会话状态,保存上下文,每次请求的时候将之前的历史消息全部发过去,但是这里面有两个问题:

  1. 请求内容会越来越大;
  2. 费用很高。

今天在Twitter上看到有人分享的一个很好的解决方案,可以借助OpenAI的embedding模型和自己的数据库,现在本地搜索数据获得上下文,然后在调用ChatGPT的API的时候,加上本地数据库中的相关内容,这样就可以让ChatGPT从你自己的数据集获得了上下文,再结合ChatGPT自己庞大的数据集给出一个更相关的理想结果。

这种模式尤其适合针对一些特定著作、资料库的搜索和问答。我想之前有人做了模拟乔布斯风格的问答应该也是基于这种模式来做的。

原理

具体解释一下它的实现原理(参考图)。

  1. 首先准备好你要用来学习的文本资料,把它变成CSV或者Json这样易于处理的格式,并且分成小块(chunks),每块不要超过8191个Tokens,因为这是OpenAI embeddings模型的输入长度限制

  2. 然后用一个程序,分批调用OpenAI embedding的API,目前最新的模式是text-embedding-ada-002,将文本块变成文本向量。

platform.openai.com/docs/guides/embeddings/what-are-embeddings

这里简单解释一下,对于OpenAI来说,要判断两段文本的相似度,它需要先将两段文本变成数字向量(vector embeddings),就像一堆坐标轴数字,然后通过数字比较可以得出一个0-1之间的小数,数字越接近1相似度越高。

所以要借助OpenAI检索相似度,将文本编码成数字向量必不可少。

(参考图一从Script到OpenAI的步骤)

  1. 需要将转换后的结果保存到本地数据库。注意一般的关系型数据库是不支持这种向量数据的,必须用特别的数据库,比如Pinecone数据库,比如Postgres数据库(需要 pgvector 扩展)。

当然你保存的时候,需要把原始的文本块和数字向量一起存储,这样才能根据数字向量反向获得原始文本。

这一步有点类似于全文索引中给数据建索引。

(参考图一从Script到DB的步骤)

  1. 等需要搜索的时候,先将你的搜索关键字,调用OpenAI embedding的API把关键字变成数字向量。

(参考图一 Search App到OpenAI)

拿到这个数字向量后,再去自己的数据库进行检索,那么就可以得到一个结果集,这个结果集会根据匹配的相似度有个打分,分越高说明越匹配,这样就可以按照匹配度倒序返回一个相关结果。

(参考图一 Search App到DB的步骤)

  1. 聊天问答的实现要稍微复杂一点

当用户提问后,需要先根据提问内容去本地数据库中搜索到一个相关结果集。

(参考图一中Chat App到Search App的步骤)

然后根据拿到的结果集,将结果集加入到请求ChatGPT的prompt中。

(参考图一中Chat App到OpenAI的步骤)

比如说用户提了一个问题:“What’s the makers’s schedule?”,从数据库中检索到相关的文字段落是:“What I worked on…”和"Taste for Makers…",那么最终的prompt看起来就像这样:


[
 {
  role: "system",
  content: "You are a helpful assistant that accurately answers queries using Paul Graham's essays. Use the text provided to form your answer, but avoid copying word-for-word from the essays. Try to use your own words when possible. Keep your answer under 5 sentences. Be accurate, helpful, concise, and clear."
 },
 {
 role: "user",
 content: `Use the following passages to provide an answer
to the query: "What's the makers's schedule?"
1. What I worked on...
2. Taste for Makers...`
 }
]

这样ChatGPT在返回结果的时候,就会加上你的数据集。

项目

项目地址:<github.com/mckaywrigley/paul-graham-gpt>

有个叫LlamaIndex(原来的名字是gpt_index)的库已经把这套逻辑封装了 🔗

  • <github.com/jerryjliu/gpt_index>

解释langchain代码

from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from langchain.document_loaders import UnstructuredFileLoader

def init_knowledge_vector_store(filepath):
    embeddings = HuggingFaceEmbeddings(model_name="GanymedeNil/text2vec-large-chinese", )
    loader = UnstructuredFileLoader(filepath, mode="elements")
    docs = loader.load()

    vector_store = FAISS.from_documents(docs, embeddings)
    return vector_store

GPT: 这段代码实现了一个初始化知识向量存储的函数。 首先,它从 langchain.embeddings.huggingface 模块中导入了 HuggingFaceEmbeddings 类,用于加载 Hugging Face 的预训练模型来将文本转换为向量。

然后,它从 langchain.vectorstores 模块中导入了 FAISS 类,用于创建一个基于 Faiss 的向量存储。接下来,它从 langchain.document_loaders 模块中导入了 UnstructuredFileLoader 类,用于从指定的文件中加载未结构化的文本数据,并将其转换为文档对象。最后,它定义了一个 init_knowledge_vector_store() 函数,该函数接收一个文件路径作为参数,使用上述类和方法初始化一个基于 Faiss 的向量存储,并返回该向量存储对象。

不使用langchain包 该怎么写

GPT: 如果不使用 langchain 包,可以使用其他类似的工具库来实现相同的功能。以下是一种可能的实现方式:

from transformers import AutoTokenizer, AutoModel
import faiss
import numpy as np
import torch

def init_knowledge_vector_store(filepath):
    # Load the Hugging Face model and tokenizer
    model_name = "GanymedeNil/text2vec-large-chinese"
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModel.from_pretrained(model_name)

    # Load the documents from the file
    with open(filepath, "r", encoding="utf-8") as f:
        texts = f.readlines()

    # Convert the documents to vectors using the Hugging Face model
    vectors = []
    for text in texts:
        inputs = tokenizer(text, return_tensors="pt")
        with torch.no_grad():
            output = model(**inputs)[0]
        vector = output.mean(dim=1).squeeze().numpy()
        vectors.append(vector)

    # Convert the vectors to a Faiss index
    index = faiss.IndexFlatIP(model.config.hidden_size)
    vectors = np.array(vectors).astype("float32")
    index.add(vectors)

    return index

该函数与之前的 init_knowledge_vector_store() 函数相似,但使用了 transformers 库中的 AutoTokenizer 和 AutoModel 类来加载和使用 Hugging Face 的预训练模型,以及使用了 faiss 库中的方法来创建一个基于 Faiss 的向量存储。

faiss.IndexFlatIP是什么意思

GPT: faiss.IndexFlatIP 是 Faiss 库中的一种索引类型,它是基于内积相似度来计算向量之间的相似度,因此它的全称是 “Index Flat with Inner Product”。它是 Faiss 库中最基本的索引类型之一,它可以在向量库中高效地进行向量搜索。

在 faiss.IndexFlatIP 中,每个向量都会被存储为一个 Faiss 中的矩阵,其中每一行表示向量中的一个特征。在搜索时,如果我们想要找到与查询向量最相似的向量,我们可以将查询向量与存储的向量执行内积操作,并找到内积值最大的向量作为结果返回。

因此,使用 faiss.IndexFlatIP 可以有效地在向量库中进行搜索,是构建基于向量相似度的搜索引擎的重要工具之一。

内积相似度是怎么计算的

GPT: 内积相似度是一种衡量两个向量之间相似度的方法,也称为点积相似度。在数学上,两个向量 A 和 B 的内积相似度可以表示为它们之间的内积(dot product)除以它们的模长的乘积,即: similarity = A ∙ B / (||A|| * ||B||) 其中,A ∙ B 表示向量 A 和 B 的内积,||A|| 表示向量 A 的模长,||B|| 表示向量 B 的模长。

在计算机中,内积相似度可以通过向量的坐标值进行计算。例如,对于两个长度为 n 的向量 A 和 B,它们的内积相似度可以表示为: similarity = sum(A[i] * B[i]) / (sqrt(sum(A[i]^2)) * sqrt(sum(B[i]^2))) 其中,i 的取值范围为 1 到 n。

在 Faiss 库中,faiss.IndexFlatIP 使用内积相似度来计算向量之间的相似度,因此可以使用内积相似度来进行向量搜索。



请遵守《互联网环境法规》文明发言,欢迎讨论问题
扫码反馈

扫一扫,反馈当前页面

咨询反馈
扫码关注
返回顶部