为了账号安全,请及时绑定邮箱和手机立即绑定

稠密与稀疏嵌入的舞蹈:LangChain-Milvus中的混合搜索技巧

如何在langchain-milvus里创建和搜索多个向量存储库

这篇博客文章是由Omri LevyOhad Eytan共同撰写的,作为我们在以色列IBM研究院的工作的一部分。

简介

最近,我们在IBM Research需要在Milvus向量数据库中使用混合搜索功能。由于我们已经在使用LangChain框架,我们决定撸起袖子,为langchain-milvus贡献所需的代码,以启用混合搜索功能。具体来说,这包括对稀疏嵌入支持(PR)和多向量搜索功能(PR)的支持,通过LangChain接口实现。

本文将简单说明密集嵌入和稀疏表示之间的区别,以及如何通过混合搜索利用这两种表示方式。我们还将通过代码示例来展示如何在_langchain-milvus_中使用这些新特性。

要使用这篇博客里提到的代码,你需要安装一些库。

以下是安装所需包的命令:

    pip install langchain_milvus==0.1.6  # 安装 langchain_milvus 版本 0.1.6
    pip install langchain-huggingface==0.1.0  # 安装 langchain-huggingface 版本 0.1.0
    pip install "pymilvus[model]==2.4.8"  # 安装 pymilvus[model] 版本 2.4.8

导入这些:

    从langchain_huggingface导入HuggingFaceEmbeddings  
    从langchain_milvus.utils.sparse导入BM25SparseEmbedding  
    从langchain_milvus.vectorstores导入Milvus

你也可以在这个 gist 中查看和克隆整个代码。

走吧。

密集向量嵌入

最常用的向量存储方式是使用密集向量。我们通常用预训练的模型将数据(比如文本,图像等)转换成高维向量,并将其存储在向量数据库中。这些向量包含几百(甚至上千)个维度,每个维度都是一个浮点数。通常,这些向量的每个维度都有非零值,因此被称为“密集”。给定一个查询,我们使用相同的模型对其进行嵌入,向量库会根据相似度找出相关数据。使用 langchain-milvus ,只需几行代码。我们现在来看看具体如何操作。

首先,我们使用来自HuggingFace的sentence-transformers/all-MiniLM-L6-v2模型来定义向量存储库。

    dense_embedding = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")  
    vector_store = Milvus(  
        embedding_function=dense_embedding,  
        connection_args={"uri": "./milvus_dense.db"}, # 为了简化,我们使用了milvus-lite,  
        auto_id=True,  
    )

接下来,我们将数据插入,如下所述:向量数据库。

    document = [  
        "今天白天很热,但晚上很冷",  
        "在以色列,Hot 是一家每周七天提供节目的电视供应商",  
    ]  
    vector_store.add_texts(documents)

幕后,每个文档都会使用我们提供的模型来嵌入成一个向量,并与文本一起存储。

最后,我们可以搜索查询并打印出我们得到的结果:

    query = "天气怎么样?很热吗?"  
    dense_output = vector_store.similarity_search(query=query, k=1)  
    print(f"稠密嵌入搜索结果:\n{dense_output[0].page_content}\n")  

    # 输出: 稠密嵌入搜索结果: 今天白天很暖和,但晚上却很冷

在这里,查询是嵌入,向量库进行(通常是近似的)相似度搜索,并返回它找到的最相似的内容。

密集嵌入模型(或称为密集表示模型)被训练来捕捉数据的语义信息并在多维空间中表示这些信息。其优势显而易见——它使语义搜索成为可能,这意味着搜索结果是根据查询的语义来确定的。但是有时候这还不够。如果你想找寻特定关键词,或者像人名这样的词,它们本身没有太多的语义,语义搜索可能会误导你,这种方法在这种情况下就不起作用了。

稀疏表示

在大型语言模型还没有成为流行趋势之前,以及预训练模型还不像现在这么流行的时候,搜索引擎使用了诸如[TF-IDF]这样的传统方法,或者其现代改进版[BM25](因其在Elastic中的应用而为人所知),来搜索相关数据。使用这些方法,维度的数量等于词汇表的大小(通常是数万,远大于密集向量空间),每个条目代表一个关键词在文档中的相关性,同时考虑了该词在文档集合中的频率及其稀有性。对于每个数据点,大多数条目都是零(因为这些词没有出现),因此称为“稀疏”。尽管内部实现不同,但通过使用langchain-milvus 接口,它变得非常相似。让我们看看它的实际应用:

sparse_embedding = BM25稀疏嵌入(corpus=documents)  
vector_store = Milvus(  
    embedding_function=sparse_embedding,  
    connection_args={"uri": "./milvus_sparse.db"},  
    auto_id="自动ID",  
)  
vector_store.add_texts(文档)  

query = "Hot在周末是否涵盖天气变化?"  
sparse_output = vector_store.similarity_search(query=query, k=1)  
print(f"稀疏嵌入结果:\n{sparse_output[0].page_content}\n")  

# 输出: 稀疏嵌入结果:  
#         在以色列,Hot是一个电视提供商,每周七天播出

BM25 对于精确关键词匹配非常有效,对于缺乏明确语义含义的术语或名称特别有用。然而,它无法理解查询的意图,在许多需要理解语义的情况下表现较差。

注意:术语“稀疏嵌入法”也指的是高级方法,如 SPLADE 或 Elastic Elser。这些方法也可以与 Milvus 结合使用,并集成到混合搜索中。
在混合搜索中,这些方法可以被集成。

这张图片是作者自己做的

混搜

如果你把上述两个例子的查询对调,并用一个的嵌入去处理另一个,两者都会产生错误的结果。这表明每种方法都有其长处,但也有其短处。混合搜索结合了两者,旨在发挥两者的长处。通过用密集和稀疏的嵌入来索引数据,我们可以执行同时兼顾语义相关性和关键词匹配的搜索,根据自定义的权重来平衡结果。虽然内部实现更复杂,但 langchain-milvus 让它变得非常容易使用。让我们来看看它是怎么工作的:

# 创建一个向量存储实例
vector_store = Milvus(  
    embedding_function=[  
        sparse_embedding,  
        dense_embedding,  
    ],  
    connection_args={"uri": "./milvus_hybrid.db"},   
    # 自动分配ID
    auto_id=True,  
)  
# 添加文本
vector_store.add_texts(documents)

在这个设置中,同时使用了稀疏和密集的嵌入。让我们以相等的权重测试混合查找。

    query = "周末期间,Hot是否涵盖天气变化?"  
    hybrid_output = vector_store.similarity_search(  
        query=query,  
        k=1,  
        ranker_type="weighted",  
        ranker_params={"weights": [0.49, 0.51]},  # 结合这两种结果!  
    )  
    print(f"混合搜索结果如下:\n{hybrid_output[0].page_content}")  

    # 输出:混合搜索结果如下:  
    #       在以色列,Hot 是一家提供7天24小时广播服务的电视提供商

这是通过每个嵌入搜索类似结果,根据每个嵌入的得分进行加权,然后返回加权得分最高的结果。这表明,当我们稍微增加稠密嵌入的影响程度时,我们得到了期望的结果。同样的,对于第二个查询也得到了我们想要的结果。

如果我们更看重密集嵌入,我们会再次得到不相关的结果,就像仅使用密集嵌入那样。

    query = "什么时候和什么地方Hot最活跃?"  
    hybrid_output = vector_store.similarity_search(  
        query=query,  
        k=1,  
        ranker_type="weighted",  
        ranker_params={"weights": [0.2, 0.8]},  # 注:权重已调整  
    )  
    print(f"混合搜索的结果如下:\n{hybrid_output[0].page_content}")  

    # 输出:混合搜索的结果如下:  
    #         今天白天非常热,但晚上却很冷

找到合适的稠密与稀疏平衡并不是一件简单的事情,这也属于更广泛的超参数优化问题的一部分。正在研究的工具和方法旨在解决这些问题,在这一领域中,例如IBM的AutoAI for RAG工具(详情见这里),它在这一领域里也发挥着重要作用。

还有许多其他方式可以调整和使用混合搜索方法。例如,如果每个文档都有一个关联的标题,你可以使用两个密集嵌入模型(可能使用不同的模型)——一个用于标题,另一个用于文档内容——并对这两个索引进行混合搜索。Milvus 当前支持多达 10 个不同的向量字段,为复杂的应用场景提供了灵活性。还有索引和重排序方法的配置。你可以参考 Milvus 文档 来了解可用的参数和选项。

结束语。

借助 LangChain,现在可以轻松地将 Milvus 的多向量搜索能力集成到您的应用程序中。这为您在应用程序中应用不同的搜索策略打开了新的可能性,使得根据特定用例调整搜索逻辑更加容易。对我们来说,这是一个为开源项目贡献力量的好机会。我们日常使用的许多库和工具都是开源的,回馈社区感觉很好。希望它也能对其他人有帮助。

最后,特别要感谢 Erick FriisCheng Zilangchain-milvus 这个项目以及他们在这些 PR 中的努力,没有他们的帮助,这个项目不可能完成。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消