Text Embedding Models
如何将一段Document转换成向量存储到向量数据库中,首先需要了解Langchain提供了哪些将文本转换成向量的model,langchian提供了很多将自然语言转换成向量的的模型,如下图所示,除了下图列举的model,更多支持的model可参考官网信息。
这里将以Langchain提供的SentenceTransformerEmbeddings和OpenAIEmbeddings为例子,通过实际代码来看看如何把一段自然语言转换成向量。SentenceTransformers 嵌入通过 HuggingFaceEmbeddings 集成进行调用,langchain还为那些更熟悉直接使用该包的用户添加了 SentenceTransformerEmbeddings 的别名,所以,本质上,SentenceTransformerEmbeddings和HuggingFaceEmbeddings是同一个内容,查看源代码也可以看到SentenceTransformerEmbeddings里面只是将HuggingFaceEmbeddings赋值给它而已,源代码如下图所示:
所以,调用HuggingFaceEmbeddings和调用SentenceTransformerEmbeddings是一样的。下面的代码中调用HuggingFaceEmbeddings(),传入使用的model名称,这里使用的是"all-MinLM-L6-v2",通过调用embed_query(text)就能得到文本的向量数据。
import os
import openai
import numpy as np
from langchain.embeddings import HuggingFaceEmbeddings, SentenceTransformerEmbeddings
from langchain.embeddings import OpenAIEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
text = "hello"
query_result = embeddings.embed_query(text)
print(query_result)
print(type(query_result))
data = np.array(query_result)
print(data.shape)
生成的向量信息如下图所示,结果信息是一个List,长度是384,可以看到通过一个长度是384个特征的值来表示上面的hello这个文字。
接着,换成OPENAIEmbeddings再看看结果如何?
openaiEmbeddings = OpenAIEmbeddings(
openai_api_key=os.environ.get("OPENAI_API_KEY"))
result = openaiEmbeddings.embed_query(text)
print(result)
print(type(result))
print(np.array(result).shape)
生成的向量信息如下图所示,结果信息仍然是一个List,长度是1536,这个长度为1536个特征的值来表示上面的hello这个文字。
Text Splitters
当对一段很长的文档进行存储的时候,都需要对文档进行拆分,Langchain提供了多种对文本进行拆分的方式,具体如下图所示,以第一种为例子,以文档的\n为默认的分隔符,Split by tokens中的tiktoken is a fast BPE tokenizer created by OpenAI.
为什么一定要把文档拆分成不同chunks呢?因为只有进行了合理拆分,在后续提问的时候,才能找出与问题最贴近的chunk进行回答。那如何保证拆分合理?即拆分出来的句子要保证语意尽量不丢失。这里有几个参数来控制,一个是spliter,对于txt文本类型,默认是换行符号,换行符代表了一个段落的结束,另外一个是chunk-size,在拆分的过程中会尽量保证拆分的chunk大小和设置的相近,另外,为了不丢失不同chunk间的连续性,还可以设置不同chunk间的重叠部分,即chunk_overlap.
下面这段代码就是对Document进行的拆分,模型按换行符进行拆分chunk.要实现Document的拆分,首先调用TextLoader加载文件,再调用CharacterTextSplitter进行拆分。
from langchain.text_splitter import CharacterTextSplitter
from langchain.document_loaders import TextLoader
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
import numpy as np
from langchain.llms import OpenAI
loader = TextLoader('basic/data/state_of_union.txt')
documents = loader.load()
text_spliter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_spliter.split_documents(documents)
print(texts)
得到的内容如下图所示,可以看到page_content里面的内容都安装\n进行拆分。
VectorStore
拆分完成后,就可以调用Langchain提供的向量存储库进行存储,如下图所示,使用的是Chroma。Chroma是一种开源的向量数据库,旨在为开发人员和各种规模的组织提供构建基于大型语言模型(LLM)的应用所需的资源。 它为开发人员提供了可扩展和高效的解决方案,用于存储、搜索和提取多维向量。将拆分后的文本向量存储到vectorstore后,可以直接调用similarity_search_with_score(query=query)查询问题,通过计算向量的相似性得到问题的答案。
embeddings = OpenAIEmbeddings()
docSearch = Chroma.from_documents(texts, embeddings)
query = "what did the president say about Putin?"
result = docSearch.similarity_search_with_score(
query=query)
print(len(result))
print(result[0])
这里通过相似度得到问题的答案,结果如下图所示,默认是给4个答案,相似度越高,放得越靠前,查看result[0]的值,可以看到相似度得分才0.33分,这里用的是余弦定理计算相识度。
在自然语言处理(NLP)和信息检索等领域,余弦相似度常常用于计算文本或向量之间的相似度。余弦相似度是一种用于衡量两个向量之间的夹角的指标,通常用于比较它们的方向和相对位置。分数与相似度之间的关系是这样的:余弦相似度范围: 余弦相似度的取值范围在-1到1之间。具体来说,余弦相似度为1表示两个向量的方向完全相同,夹角为0度,它们非常相似。余弦相似度为-1表示两个向量的方向完全相反,夹角为180度,它们非常不相似。余弦相似度为0表示两个向量之间没有明显的相似性或关系。
RetrievalQA Chain
除了直接调用向量存储库的方法计算相似度外,还可以调用Langchain封装的RetrievalQA chain。
qa_chain = RetrievalQA.from_chain_type(
llm=OpenAI(), chain_type="stuff", retriever=docSearch.as_retriever())
rs = qa_chain.run(query)
print(rs)
得到的结果如下图所示,可以看到通过大模型查询问题,得到的内容准确率更高一些。
上面的例子中使用的VectorDB是Chroma,实际Langchain支持和多种VectorDB进行集成,如下图所示,更多VectorDB可查看官网信息。
另外,上面用到的RetriveQA chain中有一个字段是chain type,上面用的stuff类型,表示将所有内容组合成一个文档,除此之外,还支持
- Map Reduce
将所有块与问题一起传递给语言模型,获取回复,使用另一个语言模型调用将所有单独的回复总结成最终答案,它可以在任意数量的文档上运行。可以并行处理单个问题,同时也需要更多的调用。它将所有文档视为独立的
- Refine
用于循环许多文档,际上是迭代的,建立在先前文档的答案之上,非常适合前后因果信息并随时间逐步构建答案,依赖于先前调用的结果。它通常需要更长的时间,并且基本上需要与Map Reduce一样多的调用
- Map Re-rank
对每个文档进行单个语言模型调用,要求它返回一个分数,选择最高分,这依赖于语言模型知道分数应该是什么,需要告诉它,如果它与文档相关,则应该是高分,并在那里精细调整说明,可以批量处理它们相对较快,但是更加昂贵
总结
将一份Document或者多份document进行向量化,然后存储到vectorstoredb中,在基于存储的向量上进行提问,也就是基于Document中的上下文进行提问,总体过程分为三部分,如下图所示。
以上就是对“基于存储的文本向量,进行问题检索”的实现过程的详细介绍。