Text embedding models
- 文本嵌入模型
检索的另一个关键部分是为文档创建嵌入。
Embeddings 类是设计用于与文本嵌入模型交互的类。
Embeddings创建一段文本的矢量表示,这样我们就可以在向量空间中思考文本,并执行语义搜索之类的操作,在向量空间中查找最相似的文本片段。
LangChain中的Embeddings基类提供了两种方法:
- 用于嵌入文档,采用多个文本作为输入
- 用于嵌入查询,采用单个文本作为输入
将它们作为两种单独方法的原因是,某些嵌入提供程序对文档(要搜索的)与查询(搜索查询本身)有不同的嵌入方法。
from langchain_openai import OpenAIEmbeddings
embeddings_model = OpenAIEmbeddings()
-
embed_documents
嵌入文本列表
embeddings = embeddings_model.embed_documents( [ "Hi there!", "Oh, hello!", "What's your name?", "My friends call me World", "Hello World!" ] ) len(embeddings), len(embeddings[0])
-
embed_query
嵌入单个查询,嵌入一段文本是为了与其他嵌入的文本进行比较。
embedded_query = embeddings_model.embed_query("What was the name mentioned in the conversation?") embedded_query[:5]
CacheBackedEmbeddings
embeddings可以被存储或临时缓存以避免需要重新计算它们。
缓存embeddings可以使用 CacheBackedEmbeddings
来完成。支持缓存的嵌入器是嵌入器的包装器,它将嵌入缓存在键值存储中。对文本进行哈希处理,并将哈希值用作缓存中的密钥。
初始化 CacheBackedEmbeddings
的主要支持方式是 from_bytes_store
。它需要以下参数:
- underlying_embedder(底层嵌入器):用于嵌入的嵌入器。
- document_embedding_cache(文档嵌入缓存):任何用于缓存文档嵌入的
ByteStore
。 - batch_size:(optional, defaults to
None
)要更新嵌入的文档数量。 - namespace:(optional, defaults to
""
) 用于文档缓存的命名空间,该命名空间用于避免与其他缓存发生冲突。例如,将其设置为所使用的嵌入模型的名称。
请务必设置命名空间参数,以避免使用不同嵌入模型嵌入的相同文本发生冲突。
from langchain.embeddings import CacheBackedEmbeddings
Using with a Vector Store
首先,让我们看一个使用本地文件系统存储嵌入并使用 FAISS 矢量存储进行检索的示例。
%pip install --upgrade --quiet langchain-openai faiss-cpu
from langchain.storage import LocalFileStore
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
underlying_embeddings = OpenAIEmbeddings()
store = LocalFileStore("./cache/")
cached_embedder = CacheBackedEmbeddings.from_bytes_store(
underlying_embeddings, store, namespace=underlying_embeddings.model
)
嵌入之前缓存为空:
list(store.yield_keys())
[]
加载文档,将其分割成块,嵌入每个块并将其加载到向量存储中。
raw_documents = TextLoader("../../state_of_the_union.txt").load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
documents = text_splitter.split_documents(raw_documents)
创建向量存储:
%%time
db = FAISS.from_documents(documents, cached_embedder)
CPU times: user 218 ms, sys: 29.7 ms, total: 248 ms
Wall time: 1.02 s
如果我们尝试再次创建向量存储,它会快得多,因为它不需要重新计算任何嵌入。
%%time
db2 = FAISS.from_documents(documents, cached_embedder)
CPU times: user 15.7 ms, sys: 2.22 ms, total: 18 ms
Wall time: 17.2 ms
以下是创建的一些嵌入:
list(store.yield_keys())[:5]
['text-embedding-ada-00217a6727d-8916-54eb-b196-ec9c9d6ca472',
'text-embedding-ada-0025fc0d904-bd80-52da-95c9-441015bfb438',
'text-embedding-ada-002e4ad20ef-dfaa-5916-9459-f90c6d8e8159',
'text-embedding-ada-002ed199159-c1cd-5597-9757-f80498e8f17b',
'text-embedding-ada-0021297d37a-2bc1-5e19-bf13-6c950f075062']
Swapping the ByteStore
- 交换字节存储
为了使用不同的 ByteStore
,只需在创建 CacheBackedEmbeddings
时使用它即可。下面,我们创建一个等效的缓存嵌入对象,但使用非持久性InMemoryByteStore
代替:
from langchain.embeddings import CacheBackedEmbeddings
from langchain.storage import InMemoryByteStore
store = InMemoryByteStore()
cached_embedder = CacheBackedEmbeddings.from_bytes_store(
underlying_embeddings, store, namespace=underlying_embeddings.model
)
Vector stores
- 矢量仓库
存储和搜索非结构化数据的最常见方法之一是将其嵌入,并存储生成的嵌入向量,然后在查询时嵌入非结构化查询并检索与嵌入查询“最相似”的嵌入向量。
矢量存储负责存储嵌入数据并执行矢量搜索。
使用向量存储的一个关键部分是创建要放入其中的向量,这通常是通过嵌入创建的。
Asynchronous operations
- 异步操作
矢量存储通常作为需要一些 I/O 操作的单独服务运行,因此它们可能会被异步调用。这样会带来性能优势,因为不会浪费时间等待外部服务的响应。如果您使用异步框架(例如 FastAPI),这一点也可能很重要。
LangChain支持向量存储的异步操作。所有方法都可以使用其异步对应方法来调用,前缀 a
表示异步。
Qdrant
是一个向量存储,它支持所有异步操作,因此将在下例中使用它。
pip install qdrant-client
from langchain_community.vectorstores import Qdrant
-
异步创建向量存储
db = await Qdrant.afrom_documents(documents, embeddings, "http://localhost:6333")
-
相似性搜索
query = "What did the president say about Ketanji Brown Jackson" docs = await db.asimilarity_search(query) print(docs[0].page_content)
-
通过vector进行相似性搜索
embedding_vector = embeddings.embed_query(query) docs = await db.asimilarity_search_by_vector(embedding_vector)
Maximum marginal relevance search (MMR)
- 最大边际相关性搜索
最大边际相关性优化了所选文档之间查询的相似性和多样性。异步 API 也支持它。
query = "What did the president say about Ketanji Brown Jackson"
found_docs = await qdrant.amax_marginal_relevance_search(query, k=2, fetch_k=10)
for i, doc in enumerate(found_docs):
print(f"{i + 1}.", doc.page_content, "\n")