
在构建基于向量相似度搜索的rag(retrieval-augmented generation)系统时,选择合适的向量数据库和嵌入模型至关重要。然而,开发者在使用langchain结合faiss和特定嵌入模型(如bge)时,可能会遇到一个常见困惑:即使查询文本与向量库中存储的文本完全一致,similarity_search_with_score()方法返回的“相似度分数”却并非预期的完美匹配值(如余弦相似度为1.0或l2距离为0.0),而是出现一个非零值(例如0.9)。这通常源于对faiss内部距离度量方式的误解以及嵌入模型特性带来的细微差异。
FAISS(Facebook AI Similarity Search)是一个高效的相似度搜索库,它底层主要基于L2距离(欧几里得距离)进行向量间的相似度计算。L2距离衡量的是多维空间中两点之间的直线距离。
与L2距离相对的是余弦相似度(Cosine Similarity),它衡量的是两个向量在方向上的接近程度,与向量的长度无关。
当嵌入向量被标准化(即归一化为单位向量)后,L2距离和余弦相似度之间存在直接的数学关系: L2_distance^2 = 2 * (1 - cosine_similarity) 这意味着,如果余弦相似度为1.0(完美匹配),则L2距离为0.0。反之,如果L2距离为0.0,则余弦相似度为1.0。
Langchain的FAISS.similarity_search_with_score()方法在与FAISS交互时,其返回的score通常就是FAISS计算出的原始L2距离。因此,当我们期望一个“完美匹配”时,L2距离应趋近于0。
不同的嵌入模型在生成向量时具有各自的特性,这可能会影响到完全相同文本的嵌入向量是否能做到“完美一致”。
考虑使用BAAI/bge-large-zh-v1.5模型作为嵌入器,并设置normalize_embeddings=True进行向量标准化。
from langchain.embeddings import HuggingFaceBgeEmbeddings
from langchain.vectorstores import FAISS
from langchain.docstore.document import Document
# 1. 初始化BGE嵌入模型
model_name = "BAAI/bge-large-zh-v1.5"
model_kwargs = {'device': 'cuda'} # 或 'cpu'
encode_kwargs = {'normalize_embeddings': True} # 设置为True以归一化嵌入向量
bge_model = HuggingFaceBgeEmbeddings(
model_name=model_name,
model_kwargs=model_kwargs,
encode_kwargs=encode_kwargs,
cache_folder="../model/", # 模型缓存路径
)
# 2. 创建或加载FAISS向量库
# 假设我们已经有一个包含 '无纸化发送失败?' 的FAISS数据库
# 为了演示,我们先创建一个简单的数据库
docs = [Document(page_content='无纸化发送失败?'), Document(page_content='凭证打包失败?')]
# 在实际应用中,db可能从文件加载:db = FAISS.load_local('../dataset/bge_faiss_db', embeddings=bge_model, index_name='index')
db_bge = FAISS.from_documents(docs, bge_model)
# 3. 执行相似度搜索
query = '无纸化发送失败?'
res_bge = db_bge.similarity_search_with_score(query, k=3)
print("BGE模型搜索结果 (L2距离):")
print(res_bge)预期结果分析: 在上述BGE模型的案例中,即使查询文本与文档内容完全相同,用户观察到的L2距离为0.9069208。这个值对于一个理论上应该为0的完美匹配来说,显得异常高。这可能由以下原因导致:
作为对比,我们来看使用OpenAI Embeddings时的表现。OpenAI Embeddings在处理相同文本时,通常能保证生成完全相同的向量,从而使得L2距离为0。
from langchain.document_loaders import TextLoader
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.docstore.document import Document
# 1. 初始化OpenAI嵌入模型
# 确保已设置OPENAI_API_KEY环境变量
embeddings_openai = OpenAIEmbeddings()
# 2. 创建一个包含测试文本的FAISS向量库
# 假设我们有一个text.txt文件,内容为 '无纸化发送失败?'
# 为了演示,我们直接创建文档对象
docs_openai = [Document(page_content='无纸化发送失败?')]
db_openai = FAISS.from_documents(docs_openai, embeddings_openai)
# 3. 执行相似度搜索
query_openai = '无纸化发送失败?'
res_openai = db_openai.similarity_search_with_score(query_openai, k=3)
print("\nOpenAI模型搜索结果 (L2距离):")
print(res_openai)
# 4. 尝试一个略微不同的查询
query_openai_slight_diff = '纸化发送失败?'
res_openai_slight_diff = db_openai.similarity_search_with_score(query_openai_slight_diff, k=3)
print("\nOpenAI模型搜索结果 (略微不同查询的L2距离):")
print(res_openai_slight_diff)预期结果分析: 使用OpenAI Embeddings时,对于完全相同的查询,L2距离通常为0.0。而对于略有差异的查询,L2距离会是一个较小的非零值(例如0.08518691),这符合L2距离的定义和预期行为。
从上述对比中可以看出,BGE模型在特定场景下为完全相同的文本返回了非零的L2距离,而OpenAI模型则返回了0.0。这表明问题可能出在BGE模型本身生成的向量的精确度或一致性上。
优化建议:
在Langchain与FAISS进行相似度搜索时,理解FAISS默认使用L2距离至关重要。对于完全相同的文本,理想的L2距离应为0。如果遇到非零距离,尤其是一个相对较大的值(如0.9),这通常不是Langchain或FAISS的错误,而是嵌入模型在处理相同文本时未能生成完全一致的向量,或存在浮点数精度问题。通过尝试不同的嵌入模型,并根据实际情况调整相似度阈值,可以更好地解决这类问题,从而构建出更健壮、更符合预期的向量相似度搜索系统。
以上就是Langchain FAISS相似度计算深度解析:理解距离度量与嵌入模型的影响的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号