改进RAG:利用混合搜索与重排序优化检索效果(含代码示例)

B站影视 电影资讯 2025-04-02 14:55 1

摘要:RAG 通过结合信息检索和语言生成能力,让模型在生成文本时能够参考相关的外部知识,从而生成更准确、更具针对性的内容。然而,在实际应用中,RAG(企业 RAG 准确性提升全流程指南:从数据提取到精准检索) 面临着诸多挑战,其中准确检索相关信息是关键问题之一。本文

RAG 通过结合信息检索和语言生成能力,让模型在生成文本时能够参考相关的外部知识,从而生成更准确、更具针对性的内容。然而,在实际应用中,RAG(企业 RAG 准确性提升全流程指南:从数据提取到精准检索) 面临着诸多挑战,其中准确检索相关信息是关键问题之一。本文将围绕如何通过混合搜索与重排序技术来提升 RAG 的检索效果展开深入探讨。

在 RAG 的实际应用中,检索环节存在诸多难点。其中,精确匹配特定标识符(ID)是一个典型问题。许多应用场景中,数据通常会被赋予唯一的 ID,这些 ID 可能包含数字、字母等复杂组合,长度往往在 10 个字符以上。在检索时,如果仅依靠基于向量的语义搜索,如使用 PGVector 存储嵌入向量进行相似性检索,会出现检索结果不准确的情况。

语义搜索基于向量之间的软相似性来对检索结果进行排序,即通过计算向量之间的相似度(如余弦相似度)来确定文档与查询的相关程度,并返回相似度最高的前 k 个结果。这种方式在处理语义相近的文本时表现出色,但对于精确匹配 ID 这类需要严格一致性的任务却力不从心。例如,当查询 “1234567890XYZ” 时,语义搜索可能会返回包含 “1234567890ABC” 的文本块,因为从语义相似度的角度来看,这两个 ID 具有一定的相似性,而真正匹配的 “1234567890XYZ” 可能由于其他因素(如向量表示的细微差异)被排在较低的位置,甚至被排除在检索结果之外。这一问题严重影响了 RAG 系统在涉及 ID 精确匹配场景下的准确性,如在医疗记录查询(根据患者 ID 检索特定病历)、金融交易信息查询(根据交易 ID 获取交易详情)等领域,错误的检索结果可能导致严重的后果。

为了解决上述问题,混合搜索方法应运而生。这种方法结合了多种搜索技术的优势,以提高检索的准确性和全面性。具体包括以下几个步骤:

PGVector 是一个用于在 PostgreSQL 数据库中存储和查询向量数据的扩展。它允许将文本或其他数据转化为向量形式,并通过向量相似性搜索来查找相关的文档。在混合搜索的第一步,利用 PGvector 进行语义搜索,能够快速找到与查询在语义上相关的文本块。通过预先训练的嵌入函数(如使用 JinaAI 的 “jina-embeddings-v2-base-en” 模型)将文档和查询转化为向量表示,然后在数据库中进行高效的相似性检索。例如,在一个包含大量新闻文章的文档库中,当查询 “科技行业最新动态” 时,PGVector 语义搜索可以迅速返回与科技行业相关的文章片段,这些片段在语义上与查询具有较高的相关性。虽然这种方式在处理语义相似的文本时效果较好,但如前文所述,对于精确匹配 ID 存在不足,因此需要结合其他搜索方法。

PostgreSQL 提供了强大的全文搜索功能,包括精确匹配和部分匹配。在混合搜索中,这一步用于弥补语义搜索在精确匹配方面的缺陷。

精确匹配

通过使用to_tsvector和plainto_tsquery函数,确保查询的 ID 在文本块中精确出现。例如,当查询 “1234567890XYZ” 时,to_tsvector('english', content) @@ plainto_tsquery('english', '1234567890XYZ')这个查询语句会在documents表的content字段中查找完全匹配 “1234567890XYZ” 的记录。这种精确匹配方式能够准确找到包含目标 ID 的文本块,避免了语义搜索中可能出现的误判。

部分匹配

如果精确匹配没有找到结果,部分匹配则发挥作用。使用ILIKE操作符进行部分匹配,例如content ILIKE '34567890XYZ%',它可以查找包含 “1234567890XYZ” 部分内容的文本块。这对于处理 ID 的变体或拼写错误等情况非常有用。比如,当用户输入的 ID 存在轻微错误时,部分匹配可以扩大检索范围,尽可能找到相关的文本块。

在完成语义搜索和全文搜索后,需要将两个搜索的结果进行合并。由于两种搜索方式可能会返回一些重叠的文本块,因此需要进行去重处理。合并结果的过程可以通过编程实现,例如在 Python 中,可以使用列表或集合等数据结构来存储和处理搜索结果,通过比较文本块的唯一标识符(如文档 ID)或内容本身来去除重复项。合并后的结果集合包含了从语义和文本匹配两个角度与查询相关的文本块,为后续的重排序提供了更全面的基础。

Flash Re-Ranker 是一种用于提升检索结果相关性的工具。经过混合搜索得到的结果虽然包含了相关的文本块,但这些文本块的顺序可能并非最优,即与查询的相关性并非按照从高到低的顺序排列。Flash Re-Ranker 通过对合并后的结果进行重新排序,将最相关的文本块排在前面。它利用了更复杂的算法和模型,综合考虑文本的语义、词汇匹配程度、上下文等多种因素来评估文本块与查询的相关性。例如,在一个包含大量产品描述的文档库中,当用户查询特定产品 ID 时,Flash Re-Ranker 可以根据产品 ID 的匹配情况、产品描述与查询的语义相关性等因素,对混合搜索得到的结果进行重新排序,使得最符合用户需求的产品描述排在检索结果的前列。

LangChain 是一个专门用于开发基于语言模型的应用框架,它为整合各种检索和文本处理技术提供了便利。下面是使用 LangChain 实现上述混合搜索与重排序的具体步骤:

from langchain.vectorstores import PGVectorfrom jinaai import Embedding# 初始化PGVectorvector_store = PGVector( connection_string="postgresql://myuser:mypassword@localhost/mydb", embedding_function=Embedding(model="jina-embeddings-v2-base-en"))# 执行语义搜索query = "1234567890XYZ"semantic_results = vector_store.similarity_search(query, k=10)print(semantic_results)

在这段代码中,首先通过PGVector类初始化了一个向量存储对象,指定了数据库连接字符串和嵌入函数。然后,使用similarity_search方法进行语义搜索,传入查询语句和需要返回的结果数量k。

import psycopg2# 定义自定义停用词STOP_WORDS = { "a", "an", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with", "me", "my", "you", "your", "we", "our", "us", "he", "him", "his", "she", "her", "hers", "it", "its", "them", "so", "too"}def filter_stop_words(query): """从输入查询中去除停用词。""" words = query.split # 将查询拆分为单词 filtered_words = [word for word in words if word.lower not in STOP_WORDS] return " ".join(filtered_words) if filtered_words else query # 确保查询不为空def perform_full_text_search(query): conn = psycopg2.connect("dbname=mydb user=myuser password=mypassword host=localhost") cursor = conn.cursor # 从查询中过滤停用词 filtered_query = filter_stop_words(query) print(f"原始查询: {query}") print(f"过滤后的查询(不含停用词): {filtered_query}") # 全文搜索查询(现在不含停用词) cursor.execute( "SELECT * FROM documents WHERE to_tsvector('english', content) @@ plainto_tsquery('english', %s);", (filtered_query,) ) exact_results = cursor.fetchall # 回退:使用ILIKE进行部分匹配(忽略停用词) cursor.execute("SELECT * FROM documents WHERE content ILIKE %s;", (f"%{query}%",)) partial_results = cursor.fetchall # 关闭游标和连接 cursor.close conn.close return exact_results + partial_resultsquery = "I want to see details of 1234567890XYZ"retrieved_chunks = perform_full_text_search(query)print("结果:", retrieved_chunks)

这段代码首先定义了一个停用词集合STOP_WORDS,并编写了一个filter_stop_words函数用于去除查询中的停用词。perform_full_text_search函数负责执行 PostgreSQL 的全文搜索,包括精确匹配和部分匹配,并返回搜索结果。

from flashrank import FlashRankerdef re_rank_results(semantic_results, text_results): ranker = FlashRanker # 从语义结果中提取文本 semantic_texts = [{"text": doc.page_content} for doc in semantic_results] # 从文本结果元组中提取文本 text_texts = [{"text": row[1]} for row in text_results] # 假设文本在第二列 combined_results = semantic_texts + text_texts reranked_results = ranker.rank(combined_results) return reranked_resultsfinal_results = re_rank_results(semantic_results, retrieved_chunks)print(final_results)

在这部分代码中,re_rank_results函数使用 Flash Re-Ranker 对语义搜索和全文搜索的结果进行合并和重排序。首先从两个搜索结果中提取文本内容,然后将它们合并成一个列表,最后使用ranker.rank方法进行重排序并返回最终结果。

在全文搜索中,停用词的处理至关重要。停用词是指那些在文本中频繁出现但对检索意义不大的词汇,如 “a”“the”“and” 等。通过自定义停用词列表,可以根据具体应用场景的需求,灵活地添加或排除特定的词汇。在处理科技文献检索时,一些特定领域的常用词汇可能在其他场景中属于停用词,但在该领域却具有重要意义,此时就需要将这些词汇从停用词列表中排除;反之,在处理特定格式的文本(如代码注释)时,一些特殊的符号或关键字可能需要被添加到停用词列表中,以提高检索的准确性和效率。

PostgreSQL 本身提供了多种语言的内置停用词列表,并且支持创建自定义词典来进行更高级的停用词处理。合理配置 PostgreSQL 的停用词相关设置,可以进一步优化全文搜索的性能。例如,根据应用所涉及的语言和文本特点,选择合适的内置停用词列表;对于一些复杂的语言处理需求,创建自定义词典,将特定的词汇或短语标记为停用词或进行特殊的词形转换,以提高搜索的精度和效率。

停用词去除不仅可以提高搜索的准确性,还能显著提升搜索性能,尤其是在处理大规模数据集时。去除停用词后,搜索时需要处理的词汇量减少,数据库的查询负担降低,从而加快搜索速度。此外,在索引构建阶段,去除停用词也可以减少索引的大小,提高索引的构建效率和存储效率。在实际应用中,需要根据数据集的规模和特点,权衡停用词处理的复杂度和性能提升之间的关系,选择最合适的停用词处理策略。

在 RAG 技术(结合DeepSeek、FAISS与LangChain构建RAG系统)的应用中,准确检索相关信息是提升系统性能的关键。通过结合基于 PGVector 的语义搜索、PostgreSQL 全文搜索以及 Flash Re-Ranker 重排序的混合搜索方法,能够有效地解决检索特定 ID 时出现的不准确问题,提高 RAG 系统的检索精度。

本文完,记得随手点个赞、收藏和转发三连,大家感兴趣的可以关注下,后续我再研究点新东西分享给大家⭐~,更多AI功能:https://y-p.cc/?f=tt 云片AI智能创作系统,让创意无限!

来源:AIGC研究社一点号

相关推荐